diff --git a/.devcontainer/java/devcontainer.json b/.devcontainer/java/devcontainer.json
index e8ca14bb..a9abca6d 100644
--- a/.devcontainer/java/devcontainer.json
+++ b/.devcontainer/java/devcontainer.json
@@ -2,7 +2,7 @@
"name": "Jumpstart java",
"image": "mcr.microsoft.com/devcontainers/java:dev-21-jdk-bookworm",
"features": {
- "ghcr.io/devcontainers-contrib/features/maven-sdkman": {
+ "ghcr.io/devcontainers-extra/features/maven-sdkman": {
"jdkVersion": "21",
"jdkDistro": "tem"
}
@@ -10,7 +10,6 @@
"customizations": {
"vscode": {
"extensions": [
- "Codeium.codeium",
"GitHub.copilot",
"redhat.java",
"ryanluker.vscode-coverage-gutters",
diff --git a/.devcontainer/python/devcontainer.json b/.devcontainer/python/devcontainer.json
new file mode 100644
index 00000000..2fb0a6cc
--- /dev/null
+++ b/.devcontainer/python/devcontainer.json
@@ -0,0 +1,14 @@
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the
+// README at: https://github.com/devcontainers/templates/tree/main/src/python
+{
+ "name": "Jumpstart Python 3",
+ "image": "mcr.microsoft.com/devcontainers/python:2-3.14-trixie",
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "GitHub.copilot",
+ "ms-python.python"
+ ]
+ }
+ }
+}
diff --git a/.github/workflows/pattern-examples.yml b/.github/workflows/pattern-examples.yml
deleted file mode 100644
index 862b2342..00000000
--- a/.github/workflows/pattern-examples.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-name: Pattern examples
-on:
- - push
- - pull_request
-jobs:
- pattern-examples:
- runs-on: ubuntu-24.04
- strategy:
- fail-fast: false
- matrix:
- java:
- - '21'
- steps:
- - uses: actions/checkout@v4
-
- - uses: actions/setup-java@v4
- with:
- distribution: 'temurin'
- java-version: ${{ matrix.java }}
-
- - name: compile
- working-directory: topics/sw_concepts/code/pattern-examples
- run: mvn install -DskipTests=true
-
- - name: lint
- working-directory: topics/sw_concepts/code/pattern-examples
- run: mvn pmd:check
-
- - name: check format
- working-directory: topics/sw_concepts/code/pattern-examples
- run: mvn spotless:check
-
- - name: run tests
- working-directory: topics/sw_concepts/code/pattern-examples
- run: mvn test
-
- - uses: actions/upload-artifact@v4.6.2
- if: always()
- with:
- path: 'topics/sw_concepts/code/pattern-examples/target'
diff --git a/.github/workflows/python-patterns.yml b/.github/workflows/python-patterns.yml
new file mode 100644
index 00000000..6a618215
--- /dev/null
+++ b/.github/workflows/python-patterns.yml
@@ -0,0 +1,94 @@
+name: Python Design Patterns
+
+on:
+ push:
+ paths:
+ - 'topics/sw_concepts/code/**/*.py'
+ - '.github/workflows/python-patterns.yml'
+ - 'pyproject.toml'
+ pull_request:
+ paths:
+ - 'topics/sw_concepts/code/**/*.py'
+ - '.github/workflows/python-patterns.yml'
+ - 'pyproject.toml'
+
+jobs:
+ lint-and-test:
+ runs-on: ubuntu-24.04
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: ['3.14']
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install ruff
+
+ - name: Lint with ruff
+ run: |
+ # Check for linting issues
+ ruff check topics/sw_concepts/code/ --output-format=github
+
+ - name: Format check with ruff
+ run: |
+ # Check if code is formatted correctly
+ ruff format topics/sw_concepts/code/ --check
+
+ - name: Run Python pattern examples
+ run: |
+ # Run all Python files to ensure they execute without errors
+ echo "Testing abstract factory patterns..."
+ python topics/sw_concepts/code/slides/abstract-factory/abstract-factory-bad-case.py
+ python topics/sw_concepts/code/slides/abstract-factory/abstract-factory-good-case.py
+
+ echo "Testing command patterns..."
+ python topics/sw_concepts/code/slides/command/command-bad-case.py
+ python topics/sw_concepts/code/slides/command/command-good-case.py
+
+ echo "Testing dependency inversion..."
+ python topics/sw_concepts/code/slides/dependency-inversion/no-dependency-inversion.py
+ python topics/sw_concepts/code/slides/dependency-inversion/with-dependency-inversion.py
+
+ echo "Testing factory patterns..."
+ python topics/sw_concepts/code/slides/factory/factory-bad-case.py
+ python topics/sw_concepts/code/slides/factory/factory-good-case.py
+
+ echo "Testing interface segregation..."
+ python topics/sw_concepts/code/slides/interface-segregation/interface-segregation-bad-case.py
+ python topics/sw_concepts/code/slides/interface-segregation/interface-segregation-good-case.py
+
+ echo "Testing strategy patterns..."
+ python topics/sw_concepts/code/slides/strategy/strategy-bad-case.py
+ python topics/sw_concepts/code/slides/strategy/strategy-good-case.py
+
+ echo "Testing UML examples..."
+ python topics/sw_concepts/code/slides/uml/class-diagram-parts.py
+
+ echo "Testing exercise examples (where possible)..."
+ python topics/sw_concepts/code/exercise/01-intro.py
+ python topics/sw_concepts/code/exercise/05-observer-bad-case.py
+
+ echo "Testing solution examples..."
+ python topics/sw_concepts/code/solution/01-intro-refactored.py
+ python topics/sw_concepts/code/solution/02-interfaces.py
+ python topics/sw_concepts/code/solution/03-factory.py
+ python topics/sw_concepts/code/solution/04-abstract-factory.py
+ python topics/sw_concepts/code/solution/05-observer-good-case.py
+
+ echo "All Python pattern examples executed successfully!"
+
+ - name: Run type checking (if mypy is available)
+ continue-on-error: true
+ run: |
+ pip install mypy
+ mypy topics/sw_concepts/code/slides/ --ignore-missing-imports || true
diff --git a/.vscode/settings.json b/.vscode/settings.json
index b2e0277b..c58c7f25 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,11 +1,4 @@
{
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"cmake.generator": "Unix Makefiles",
- "codeium.disableSupercomplete": true,
- "codeium.enableExplainProblem": false,
- "codeium.enableInComments": false,
- "codeium.enableCodeLens": false,
- "codeium.enableConfig": {
- "*": false
- },
}
diff --git a/README.md b/README.md
index 961ca5e9..e61d47c1 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ jumpstart-docs
[](https://github.com/scs/jumpstart-docs/actions/workflows/cmake-exercise.yml)
[](https://github.com/scs/jumpstart-docs/actions/workflows/docker-exercise.yml)
[](https://github.com/scs/jumpstart-docs/actions/workflows/java-exercise.yml)
-[](https://github.com/scs/jumpstart-docs/actions/workflows/pattern-examples.yml)
+[](https://github.com/scs/jumpstart-docs/actions/workflows/python-patterns.yml)
Die SCS Jumpstart-Kurse bieten einen effizienten Einstieg in den praktischen Alltag eines Entwicklers.
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..f3bbff72
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,67 @@
+[project]
+name = "jumpstart-docs"
+version = "0.1.0"
+description = "Software concepts and design patterns examples"
+requires-python = ">=3.10"
+
+[tool.ruff]
+# Python 3.10+ compatible
+target-version = "py310"
+
+# Line length
+line-length = 100
+
+# Exclude directories
+exclude = [
+ ".git",
+ ".venv",
+ "venv",
+ "build",
+ "__pycache__",
+ "*.pyc",
+]
+
+[tool.ruff.lint]
+# Enable specific rule sets
+select = [
+ "E", # pycodestyle errors
+ "W", # pycodestyle warnings
+ "F", # pyflakes
+ "I", # isort
+ "N", # pep8-naming
+ "UP", # pyupgrade
+ "B", # flake8-bugbear
+ "C4", # flake8-comprehensions
+ "SIM", # flake8-simplify
+ "RET", # flake8-return
+ "ARG", # flake8-unused-arguments
+ "PTH", # flake8-use-pathlib
+ "ERA", # eradicate (commented-out code)
+ "PL", # pylint
+ "PERF", # perflint
+ "RUF", # ruff-specific rules
+]
+
+# Ignore specific rules
+ignore = [
+ "E501", # Line too long (handled by formatter)
+ "PLR0913", # Too many arguments
+ "PLR2004", # Magic value used in comparison
+]
+
+[tool.ruff.lint.per-file-ignores]
+# Allow unused imports in __init__.py files
+"__init__.py" = ["F401"]
+
+[tool.ruff.format]
+# Use double quotes for strings
+quote-style = "double"
+
+# Indent with spaces
+indent-style = "space"
+
+# Unix line endings
+line-ending = "auto"
+
+# Enable auto-formatting of code examples in docstrings
+docstring-code-format = true
diff --git a/topics/sw_concepts/code/exercise/01-intro.py b/topics/sw_concepts/code/exercise/01-intro.py
new file mode 100644
index 00000000..f4709927
--- /dev/null
+++ b/topics/sw_concepts/code/exercise/01-intro.py
@@ -0,0 +1,60 @@
+import math
+
+
+def main() -> None:
+ # --- Order 1: Alice buys 3 Widgets and 1 Gadget ---
+ o1_total = 0.0
+ o1_total += 3 * 9.99
+ o1_total += 1 * 24.99
+ if o1_total > 50:
+ o1_total *= 0.9
+ o1_tax = o1_total * 0.19
+ o1_total += o1_tax
+ o1_total = math.floor(o1_total * 100) / 100
+ print(f"Order 1 (Alice): {o1_total} CHF")
+
+ # --- Order 2: Bob buys 5 Widgets ---
+ o2_total = 0.0
+ o2_total += 5 * 9.99
+ if o2_total > 50:
+ o2_total *= 0.9
+ o2_tax = o2_total * 0.19
+ o2_total += o2_tax
+ o2_total = math.floor(o2_total * 100) / 100
+ print(f"Order 2 (Bob): {o2_total} CHF")
+
+ # --- Order 3: Charlie buys 2 Gadgets and 4 Widgets, pays in USD ---
+ o3_total = 0.0
+ o3_total += 4 * 9.99
+ o3_total += 2 * 24.99
+ if o3_total > 50:
+ o3_total *= 0.9
+ o3_tax = o3_total * 0.19
+ o3_total += o3_tax
+ o3_total = math.floor(o3_total * 100) / 100
+ o3_total_usd = o3_total * 1.08
+ o3_total_usd = math.floor(o3_total_usd * 100) / 100
+ print(f"Order 3 (Charlie): {o3_total_usd} USD")
+
+ # --- Print receipt for Alice ---
+ print("=" * 40)
+ print("RECEIPT")
+ print("=" * 40)
+ r1_total = 0.0
+ r1_total += 3 * 9.99
+ r1_total += 1 * 24.99
+ print(f" 3x Widget @ 9.99 = {3 * 9.99}")
+ print(f" 1x Gadget @ 24.99 = {1 * 24.99}")
+ if r1_total > 50:
+ print(f" Discount (10%): -{r1_total * 0.1:.2f}")
+ r1_total *= 0.9
+ r1_tax = r1_total * 0.19
+ r1_total += r1_tax
+ r1_total = math.floor(r1_total * 100) / 100
+ print(f" Tax (19%): {r1_tax:.2f}")
+ print(f" TOTAL: {r1_total} CHF")
+ print("=" * 40)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/topics/sw_concepts/code/exercise/02-03-interfaces.py b/topics/sw_concepts/code/exercise/02-03-interfaces.py
new file mode 100644
index 00000000..e32da5d2
--- /dev/null
+++ b/topics/sw_concepts/code/exercise/02-03-interfaces.py
@@ -0,0 +1,50 @@
+from abc import ABC, abstractmethod
+from enum import StrEnum, auto
+
+
+class Washer(ABC):
+ @abstractmethod
+ def wash_clothes(self, item: str):
+ pass
+
+ @abstractmethod
+ def wash_dishes(self, item: str):
+ pass
+
+
+class MachineWasher:
+ # TODO
+ pass
+
+
+class HandWasher:
+ # TODO
+ pass
+
+
+class ThingsToWash(StrEnum):
+ PANTS = auto()
+ SHIRT = auto()
+ SPOON = auto()
+ PLATE = auto()
+ FORK = auto()
+
+
+def wash_things(washer: Washer, things: list[ThingsToWash]) -> None:
+ for thing in things:
+ if thing in (ThingsToWash.PANTS, ThingsToWash.SHIRT):
+ washer.wash_clothes(str(thing))
+ elif thing in (ThingsToWash.SPOON, ThingsToWash.PLATE, ThingsToWash.FORK):
+ washer.wash_dishes(str(thing))
+
+
+def main() -> None:
+ machine_washer = MachineWasher()
+ hand_washer = HandWasher()
+
+ wash_things(machine_washer, [ThingsToWash.PLATE, ThingsToWash.SHIRT, ThingsToWash.SPOON])
+ wash_things(hand_washer, [ThingsToWash.PANTS, ThingsToWash.FORK])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/topics/sw_concepts/code/exercise/04-abstract-factory.py b/topics/sw_concepts/code/exercise/04-abstract-factory.py
new file mode 100644
index 00000000..3d29f6cb
--- /dev/null
+++ b/topics/sw_concepts/code/exercise/04-abstract-factory.py
@@ -0,0 +1,90 @@
+from abc import ABC, abstractmethod
+from enum import StrEnum, auto
+
+
+class ClothesWasher(ABC):
+ @abstractmethod
+ def wash(self, item: str) -> None:
+ pass
+
+
+class DishWasher(ABC):
+ @abstractmethod
+ def wash(self, item: str) -> None:
+ pass
+
+
+class MachineClothesWasher(ClothesWasher):
+ def wash(self, item: str) -> None:
+ print(f"Machine washing clothes: {item}.")
+
+
+class MachineDishWasher(DishWasher):
+ def wash(self, item: str) -> None:
+ print(f"Machine washing dishes: {item}.")
+
+
+class HandClothesWasher(ClothesWasher):
+ def wash(self, item: str) -> None:
+ print(f"Hand washing clothes: {item}.")
+
+
+class HandDishWasher(DishWasher):
+ def wash(self, item: str) -> None:
+ print(f"Hand washing dishes: {item}.")
+
+
+class WasherFactory(ABC):
+ @abstractmethod
+ def create_clothes_washer(self) -> ClothesWasher:
+ pass
+
+ @abstractmethod
+ def create_dish_washer(self) -> DishWasher:
+ pass
+
+
+# TODO: Implement MachineWasherFactory and HandWasherFactory
+
+
+class ThingsToWash(StrEnum):
+ PANTS = auto()
+ SHIRT = auto()
+ SPOON = auto()
+ PLATE = auto()
+ FORK = auto()
+
+
+def wash_things(factory: WasherFactory, things: list[ThingsToWash]) -> None:
+ clothes_washer = factory.create_clothes_washer()
+ dish_washer = factory.create_dish_washer()
+
+ for thing in things:
+ if thing in (ThingsToWash.PANTS, ThingsToWash.SHIRT):
+ clothes_washer.wash(str(thing))
+ elif thing in (ThingsToWash.SPOON, ThingsToWash.PLATE, ThingsToWash.FORK):
+ dish_washer.wash(str(thing))
+
+
+def get_washer_factory(can_wash_fragile_things: bool) -> WasherFactory:
+ # TODO
+ pass
+
+
+def main() -> None:
+ factory = get_washer_factory(can_wash_fragile_things=True)
+
+ wash_things(
+ factory,
+ [
+ ThingsToWash.PLATE,
+ ThingsToWash.SHIRT,
+ ThingsToWash.SPOON,
+ ThingsToWash.PANTS,
+ ThingsToWash.FORK,
+ ],
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/topics/sw_concepts/code/exercise/05-observer-bad-case.py b/topics/sw_concepts/code/exercise/05-observer-bad-case.py
new file mode 100644
index 00000000..460952fe
--- /dev/null
+++ b/topics/sw_concepts/code/exercise/05-observer-bad-case.py
@@ -0,0 +1,80 @@
+class Theme:
+ def __init__(self) -> None:
+ self._is_dark_theme: bool = False
+
+ @property
+ def is_dark_theme(self) -> bool:
+ return self._is_dark_theme
+
+ @is_dark_theme.setter
+ def is_dark_theme(self, value: bool) -> None:
+ self._is_dark_theme = value
+ theme_name = "Dark" if value else "Light"
+ print(f"[Theme] Theme changed to {theme_name}")
+
+
+class DarkThemeButton:
+ def __init__(self, theme: Theme) -> None:
+ self.theme: Theme = theme
+ self.light_theme_button: LightThemeButton | None = None
+ self.enabled: bool = True
+
+ def on_action(self) -> None:
+ if not self.enabled:
+ return
+
+ print("Dark theme button clicked")
+ self.theme.is_dark_theme = True
+ self.set_enable(False)
+ if self.light_theme_button:
+ self.light_theme_button.set_enable(True)
+
+ def set_enable(self, enable: bool) -> None:
+ self.enabled = enable
+ status = "enabled" if enable else "disabled"
+ print(f" Dark theme button is now {status}")
+
+
+class LightThemeButton:
+ def __init__(self, theme: Theme) -> None:
+ self.theme: Theme = theme
+ self.dark_theme_button: DarkThemeButton | None = None
+ self.enabled: bool = False
+
+ def on_action(self) -> None:
+ if not self.enabled:
+ return
+
+ print("Light theme button clicked")
+ self.theme.is_dark_theme = False
+ self.set_enable(False)
+ if self.dark_theme_button:
+ self.dark_theme_button.set_enable(True)
+
+ def set_enable(self, enable: bool) -> None:
+ self.enabled = enable
+ status = "enabled" if enable else "disabled"
+ print(f" Light theme button is now {status}")
+
+
+def main() -> None:
+ theme: Theme = Theme()
+
+ dark_button: DarkThemeButton = DarkThemeButton(theme)
+ light_button: LightThemeButton = LightThemeButton(theme)
+
+ dark_button.light_theme_button = light_button
+ light_button.dark_theme_button = dark_button
+
+ print("User clicks dark theme button:")
+ dark_button.on_action()
+
+ print("\nUser clicks light theme button:")
+ light_button.on_action()
+
+ print("\nUser clicks dark theme button again:")
+ dark_button.on_action()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/topics/sw_concepts/code/pattern-examples/.gitignore b/topics/sw_concepts/code/pattern-examples/.gitignore
deleted file mode 100644
index eb5a316c..00000000
--- a/topics/sw_concepts/code/pattern-examples/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-target
diff --git a/topics/sw_concepts/code/pattern-examples/pmd-config.xml b/topics/sw_concepts/code/pattern-examples/pmd-config.xml
deleted file mode 100644
index 10bc782e..00000000
--- a/topics/sw_concepts/code/pattern-examples/pmd-config.xml
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
-
-
-
- RuleSet for jumpstart.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/topics/sw_concepts/code/pattern-examples/pom.xml b/topics/sw_concepts/code/pattern-examples/pom.xml
deleted file mode 100644
index c3d93dd1..00000000
--- a/topics/sw_concepts/code/pattern-examples/pom.xml
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
- 4.0.0
-
- ch.scs.jumpstart
- pattern-examples
- 1.0-SNAPSHOT
-
-
-
- 21
- 21
- UTF-8
- UTF-8
-
-
-
-
-
- org.junit
- junit-bom
- 5.14.3
- pom
- import
-
-
- org.mockito
- mockito-bom
- 5.22.0
- pom
- import
-
-
-
-
-
-
- org.assertj
- assertj-core
- 3.27.7
- test
-
-
- org.junit.jupiter
- junit-jupiter-api
- test
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.junit.jupiter
- junit-jupiter
- test
-
-
- org.mockito
- mockito-core
- test
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.15.0
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- 3.5.5
-
-
-
- com.diffplug.spotless
- spotless-maven-plugin
- 2.46.1
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-pmd-plugin
- 3.28.0
-
- false
- true
-
- pmd-config.xml
-
-
-
-
-
- org.jacoco
- jacoco-maven-plugin
- 0.8.14
-
-
-
- prepare-agent
-
-
-
- report
- test
-
- report
-
-
-
-
-
-
-
-
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/Abstractions.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/Abstractions.java
deleted file mode 100644
index 5122f7bf..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/Abstractions.java
+++ /dev/null
@@ -1,134 +0,0 @@
-package ch.scs.jumpstart.pattern.examples;
-
-import java.io.*;
-import java.nio.file.*;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-
-public class Abstractions {
-
- private static final int INPUT_MIN = 0;
- private static final int INPUT_MAX_TSL2550 = 544;
- private static final int INPUT_THRESHOLD = 11;
-
- private static final double OUTPUT_MIN_FACTOR = 0.2;
- private static final double OUTPUT_CHANGE_MAX_FACTOR = 0.005;
-
- private static final int[] SENSOR_VALUE_LUX_APPROX_MAP = {
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 18, 20, 22, 24, 26, 28, 30,
- 32, 34, 36, 38, 40, 42, 44, 46,
- 49, 53, 57, 61, 65, 69, 73, 77,
- 81, 85, 89, 93, 97, 101, 105, 109,
- 115, 123, 131, 139, 147, 155, 163, 171,
- 179, 187, 195, 203, 211, 219, 227, 235,
- 247, 263, 279, 295, 311, 327, 343, 359,
- 375, 391, 407, 423, 439, 455, 471, 487,
- 511, 543, 575, 607, 639, 671, 703, 735,
- 767, 799, 831, 863, 895, 927, 959, 991,
- 1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,
- 1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,
- 2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,
- 3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015
- };
-
- @SuppressWarnings({
- "PMD.CognitiveComplexity",
- "PMD.CyclomaticComplexity",
- "PMD.SystemPrintln",
- "PMD.AvoidPrintStackTrace"
- })
- public static void main(String[] args) throws IOException {
- if (args.length != 3) {
- throw new IllegalArgumentException("Wrong number of arguments");
- }
-
- String path = args[0];
- String inputPath = args[1];
- String outputPath = args[2];
- boolean opt3001 =
- Files.isSymbolicLink(Paths.get(inputPath))
- && Files.readSymbolicLink(Paths.get(inputPath))
- .toString()
- .contains("in_illuminance_input");
-
- int inputMax = INPUT_MAX_TSL2550;
- int inputLastValue = INPUT_MIN - INPUT_THRESHOLD;
- Integer outputLastValue = null;
-
- try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
- int outputMax = Integer.parseInt(reader.readLine().trim());
- int outputMin = (int) Math.ceil(outputMax * OUTPUT_MIN_FACTOR);
- int outputChangeMax = (int) Math.ceil(outputMax * OUTPUT_CHANGE_MAX_FACTOR);
-
- while (true) {
- try {
- int inputValue;
- if (opt3001) {
- try (BufferedReader inputReader = new BufferedReader(new FileReader(inputPath))) {
- inputValue = (int) Double.parseDouble(inputReader.readLine().trim());
- } catch (IOException e) {
- inputValue = INPUT_MIN;
- }
- } else {
- try (BufferedReader inputReader = new BufferedReader(new FileReader(inputPath))) {
- inputValue = Integer.parseInt(inputReader.readLine().trim());
- }
- if (0 <= inputValue && inputValue < SENSOR_VALUE_LUX_APPROX_MAP.length) {
- inputValue = SENSOR_VALUE_LUX_APPROX_MAP[inputValue];
- } else {
- inputValue = inputLastValue;
- }
- }
-
- inputValue = Math.min(inputValue, inputMax);
-
- if (Math.abs(inputValue - inputLastValue) < INPUT_THRESHOLD) {
- inputValue = inputLastValue;
- }
-
- double a = (inputValue - INPUT_MIN) / (double) (inputMax - INPUT_MIN);
- int value1 = (int) (a * (outputMax - outputMin) + outputMin);
- int outputValue = Math.min(value1, outputMax);
-
- if (outputLastValue == null) {
- outputValue = outputValue;
- } else if (outputValue >= outputLastValue) {
- outputValue = Math.min(outputValue, outputLastValue + outputChangeMax);
- } else {
- outputValue = Math.max(outputValue, outputLastValue - outputChangeMax);
- }
- int dimmedValue = outputValue;
-
- if (!Objects.equals(outputValue, outputLastValue)) {
- System.out.printf(
- "input: %4d (%4.1f%%), output: %4d (%4.1f%%), dimmed: %4d (%4.1f%%)%n",
- inputValue,
- 100 * (inputValue - INPUT_MIN) / (double) (inputMax - INPUT_MIN),
- outputValue,
- 100 * (outputValue - outputMin) / (double) (outputMax - outputMin),
- dimmedValue,
- 100 * (dimmedValue - outputMin) / (double) (outputMax - outputMin));
- System.out.flush();
- }
-
- try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputPath))) {
- outputLastValue = outputValue;
- writer.write(String.valueOf(outputValue));
- }
-
- inputLastValue = inputValue;
- } catch (IOException e) {
- if (!(e instanceof FileNotFoundException)) {
- throw e;
- }
- }
-
- TimeUnit.MILLISECONDS.sleep(10);
- }
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- }
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/AbstractionsSolution.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/AbstractionsSolution.java
deleted file mode 100644
index b028bc61..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/AbstractionsSolution.java
+++ /dev/null
@@ -1,177 +0,0 @@
-package ch.scs.jumpstart.pattern.examples;
-
-import java.io.*;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.concurrent.TimeUnit;
-
-public class AbstractionsSolution {
- private static final int INPUT_MAX_TSL2550 = 544;
- private static final double OUTPUT_MIN_FACTOR = 0.2;
- private static final double OUTPUT_CHANGE_MAX_FACTOR = 0.005;
-
- @SuppressWarnings({"PMD.AvoidPrintStackTrace"})
- public static void main(String[] args) throws IOException {
- if (args.length != 3) {
- throw new IllegalArgumentException("Wrong number of arguments");
- }
-
- String path = args[0];
- String inputPath = args[1];
- String outputPath = args[2];
- boolean opt3001 =
- Files.isSymbolicLink(Paths.get(inputPath))
- && Files.readSymbolicLink(Paths.get(inputPath))
- .toString()
- .contains("in_illuminance_input");
-
- try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
- var brightnessControl = createBrightnessControl(reader, opt3001, inputPath);
-
- while (true) {
- try {
- try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputPath))) {
- writer.write(String.valueOf(brightnessControl.update()));
- }
- } catch (IOException e) {
- if (!(e instanceof FileNotFoundException)) {
- throw e;
- }
- }
-
- TimeUnit.MILLISECONDS.sleep(10);
- }
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- private static BrightnessControl createBrightnessControl(
- BufferedReader reader, boolean opt3001, String inputPath) throws IOException {
- int outputMax = Integer.parseInt(reader.readLine().trim());
- int outputMin = (int) Math.ceil(outputMax * OUTPUT_MIN_FACTOR);
- int outputChangeMax = (int) Math.ceil(outputMax * OUTPUT_CHANGE_MAX_FACTOR);
-
- return new BrightnessControl(
- opt3001, INPUT_MAX_TSL2550, inputPath, outputMin, outputMax, outputChangeMax);
- }
-}
-
-class BrightnessControl {
- private static final int INPUT_MIN = 0;
- private static final int INPUT_THRESHOLD = 11;
-
- private static final int[] SENSOR_VALUE_LUX_APPROX_MAP = {
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 18, 20, 22, 24, 26, 28, 30,
- 32, 34, 36, 38, 40, 42, 44, 46,
- 49, 53, 57, 61, 65, 69, 73, 77,
- 81, 85, 89, 93, 97, 101, 105, 109,
- 115, 123, 131, 139, 147, 155, 163, 171,
- 179, 187, 195, 203, 211, 219, 227, 235,
- 247, 263, 279, 295, 311, 327, 343, 359,
- 375, 391, 407, 423, 439, 455, 471, 487,
- 511, 543, 575, 607, 639, 671, 703, 735,
- 767, 799, 831, 863, 895, 927, 959, 991,
- 1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,
- 1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,
- 2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,
- 3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015
- };
-
- private final boolean opt3001;
- private final int inputMax;
- private final String inputPath;
-
- private final int outputMax;
- private final int outputMin;
- private final int outputChangeMax;
-
- private int inputLastValue;
- private Integer outputLastValue = null;
-
- public BrightnessControl(
- boolean opt3001,
- int inputMax,
- String inputPath,
- int outputMin,
- int outputMax,
- int outputChangeMax) {
- this.opt3001 = opt3001;
- this.inputMax = inputMax;
- this.inputPath = inputPath;
- this.outputMax = outputMax;
- this.outputMin = outputMin;
- this.outputChangeMax = outputChangeMax;
-
- this.inputLastValue = INPUT_MIN - INPUT_THRESHOLD;
- }
-
- int update() throws IOException {
- var inputValue = getInputValue();
- var outputValue = calculateOutput(inputValue);
- var dimmedValue = getDimmedValue(outputValue);
- logValues(inputValue, outputValue, dimmedValue);
- inputLastValue = inputValue;
- outputLastValue = dimmedValue;
- return dimmedValue;
- }
-
- private int getInputValue() throws IOException {
- int inputValue;
- if (opt3001) {
- try (BufferedReader inputReader = new BufferedReader(new FileReader(inputPath))) {
- inputValue = (int) Double.parseDouble(inputReader.readLine().trim());
- } catch (IOException e) {
- inputValue = INPUT_MIN;
- }
- } else {
- try (BufferedReader inputReader = new BufferedReader(new FileReader(inputPath))) {
- inputValue = Integer.parseInt(inputReader.readLine().trim());
- }
- if (0 <= inputValue && inputValue < SENSOR_VALUE_LUX_APPROX_MAP.length) {
- inputValue = SENSOR_VALUE_LUX_APPROX_MAP[inputValue];
- } else {
- inputValue = inputLastValue;
- }
- }
-
- inputValue = Math.min(inputValue, inputMax);
-
- if (Math.abs(inputValue - inputLastValue) < INPUT_THRESHOLD) {
- inputValue = inputLastValue;
- }
- return inputValue;
- }
-
- private int calculateOutput(int inputValue) {
- double a = (inputValue - INPUT_MIN) / (double) (inputMax - INPUT_MIN);
- int value1 = (int) (a * (outputMax - outputMin) + outputMin);
- return Math.min(value1, outputMax);
- }
-
- private int getDimmedValue(int outputValue) {
- if (outputLastValue == null) {
- return outputValue;
- }
- if (outputValue >= outputLastValue) {
- return Math.min(outputValue, outputLastValue + outputChangeMax);
- } else {
- return Math.max(outputValue, outputLastValue - outputChangeMax);
- }
- }
-
- @SuppressWarnings({"PMD.SystemPrintln"})
- private void logValues(int inputValue, int outputValue, int dimmedValue) {
- System.out.printf(
- "input: %4d (%4.1f%%), output: %4d (%4.1f%%), dimmed: %4d (%4.1f%%)%n",
- inputValue,
- 100 * (inputValue - INPUT_MIN) / (double) (inputMax - INPUT_MIN),
- outputValue,
- 100 * (outputValue - outputMin) / (double) (outputMax - outputMin),
- dimmedValue,
- 100 * (dimmedValue - outputMin) / (double) (outputMax - outputMin));
- System.out.flush();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/GameLogic.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/GameLogic.java
deleted file mode 100644
index 8446f7db..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/GameLogic.java
+++ /dev/null
@@ -1,127 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.*;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import ch.scs.jumpstart.pattern.examples.checkers.movevalidator.MoveValidator;
-import ch.scs.jumpstart.pattern.examples.checkers.movevalidator.NoOtherMoveToJumpPossible;
-import ch.scs.jumpstart.pattern.examples.checkers.util.Console;
-import java.util.List;
-import java.util.Optional;
-
-@SuppressWarnings("ClassCanBeRecord")
-public class GameLogic {
- private final Console console;
- private final Board board;
- private final List moveValidators;
- private final MoveExecutor moveExecutor;
- private final NoOtherMoveToJumpPossible noOtherMoveToJumpPossible;
- private final WinCondition winCondition;
-
- public GameLogic(
- Console console,
- Board board,
- List moveValidators,
- MoveExecutor moveExecutor,
- NoOtherMoveToJumpPossible noOtherMoveToJumpPossible,
- WinCondition winCondition) {
- this.console = console;
- this.board = board;
- this.moveValidators = moveValidators;
- this.moveExecutor = moveExecutor;
- this.noOtherMoveToJumpPossible = noOtherMoveToJumpPossible;
- this.winCondition = winCondition;
- }
-
- public void run() {
- Player currentPlayer = Player.PLAYER_RED;
- while (true) {
- Player otherPlayer =
- currentPlayer == Player.PLAYER_RED ? Player.PLAYER_WHITE : Player.PLAYER_RED;
- console.print(
- currentPlayer
- + ", make your move. Or type 'undo' to go back to the start of the turn of "
- + otherPlayer);
- if (doPlayerMove(currentPlayer)) {
- return;
- }
- if (currentPlayer == Player.PLAYER_WHITE) {
- currentPlayer = Player.PLAYER_RED;
- } else {
- currentPlayer = Player.PLAYER_WHITE;
- }
- }
- }
-
- @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.CognitiveComplexity"})
- private boolean doPlayerMove(Player player) {
- BoardCoordinates startCoordinatesForMultipleJump = null;
- while (true) {
- String userInput = console.getUserInput();
- if ("undo".equals(userInput.toLowerCase().trim())) {
- try {
- Player playerOfUndoneMove = board.undoLastTurn();
- if (playerOfUndoneMove.equals(player)) {
- continue;
- } else {
- return false;
- }
- } catch (Board.NoPreviousMovesException e) {
- console.print("There were no previous moves to undo. Please make a move.");
- continue;
- }
- }
- Move move;
- try {
- move = Move.parse(player, userInput);
- } catch (IllegalArgumentException e) {
- console.print("Invalid input, try again");
- continue;
- }
- if (startCoordinatesForMultipleJump != null
- && !startCoordinatesForMultipleJump.equals(move.start())) {
- console.print("For a multiple jump move, the same piece has to be used. Try again");
- continue;
- }
- Optional pieceAtStart = board.getPieceAt(move.start());
- if (!moveValidators.stream().allMatch(moveValidator -> moveValidator.validate(move, board))) {
- console.print("Invalid move, try again");
- continue;
- }
-
- Move executedMove = moveExecutor.executeMove(move);
-
- boolean hasPlayerWon = winCondition.hasPlayerWon(player, board);
- if (hasPlayerWon) {
- console.print("Congratulations, player " + player + " has won");
- return true;
- }
-
- if (executedMove.jumpGambleResult() == JumpGambleResult.WON) {
- console.print("The gamble has been won, " + player + " can play again.");
- console.print(
- player + ", make your move. Or type 'undo' to go back to the start of your turn.");
- continue;
- }
-
- Optional pieceAtEnd = board.getPieceAt(move.end());
- boolean wasKingCreated = wasKingCreated(pieceAtStart.orElse(null), pieceAtEnd.orElse(null));
- if (wasKingCreated) {
- return false;
- }
- if (!move.isJumpMove()
- || !noOtherMoveToJumpPossible.jumpMovePossibleFrom(move.end(), board)) {
- return false;
- }
- console.print("Multiple jump move for " + player + ". Enter your next jump.");
- console.print("Or type 'undo' to go back to the start of your turn.");
- startCoordinatesForMultipleJump = move.end();
- }
- }
-
- private boolean wasKingCreated(Piece pieceAtStart, Piece pieceAtEnd) {
- if (pieceAtStart == null || pieceAtEnd == null) {
- return false;
- }
- return !pieceAtStart.isKing() && pieceAtEnd.isKing();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/Main.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/Main.java
deleted file mode 100644
index b741d19c..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/Main.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import ch.scs.jumpstart.pattern.examples.checkers.movevalidator.*;
-import ch.scs.jumpstart.pattern.examples.checkers.util.BoardPrinter;
-import ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser;
-import ch.scs.jumpstart.pattern.examples.checkers.util.Console;
-import ch.scs.jumpstart.pattern.examples.checkers.util.PointsCalculator;
-import java.util.List;
-
-public class Main {
- public static void main(String[] args) {
- GameLogic gameLogic =
- createGameLogic(Console.getInstance(), new CoinTosser(new PointsCalculator()));
-
- gameLogic.run();
- }
-
- public static GameLogic createGameLogic(Console console, CoinTosser coinTosser) {
- Board board = new Board();
- BoardPrinter boardPrinter = new BoardPrinter(console);
- board.registerObserver(boardPrinter::printBoard);
-
- MoveIsDiagonal moveIsDiagonal = new MoveIsDiagonal();
- MoveLength moveLength = new MoveLength();
- MoveIsForwardIfNotKing moveIsForwardIfNotKing = new MoveIsForwardIfNotKing();
- OpponentPieceBetweenJump opponentPieceBetweenJump = new OpponentPieceBetweenJump();
- TargetFieldEmpty targetFieldEmpty = new TargetFieldEmpty();
- NoOtherMoveToJumpPossible noOtherMoveToJumpPossible =
- new NoOtherMoveToJumpPossible(
- moveIsForwardIfNotKing, opponentPieceBetweenJump, targetFieldEmpty);
- StartPieceValid startPieceValid = new StartPieceValid();
-
- List moveValidators =
- List.of(
- moveIsDiagonal,
- moveIsForwardIfNotKing,
- moveLength,
- noOtherMoveToJumpPossible,
- opponentPieceBetweenJump,
- startPieceValid,
- targetFieldEmpty);
-
- WinCondition winCondition =
- new WinCondition(
- List.of(
- moveIsDiagonal,
- moveLength,
- moveIsForwardIfNotKing,
- opponentPieceBetweenJump,
- targetFieldEmpty,
- startPieceValid));
-
- MoveExecutor moveExecutor = new MoveExecutor(board, coinTosser, console);
- GameLogic gameLogic =
- new GameLogic(
- console, board, moveValidators, moveExecutor, noOtherMoveToJumpPossible, winCondition);
-
- console.print("Welcome to checkers");
- boardPrinter.printBoard(board);
- return gameLogic;
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/MoveExecutor.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/MoveExecutor.java
deleted file mode 100644
index 61f0c6c0..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/MoveExecutor.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser.Result.HEADS;
-import static ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser.Result.TAILS;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.JumpGambleResult;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser;
-import ch.scs.jumpstart.pattern.examples.checkers.util.Console;
-
-@SuppressWarnings("ClassCanBeRecord")
-public class MoveExecutor {
- private final Board board;
- private final CoinTosser coinTosser;
- private final Console console;
-
- public MoveExecutor(Board board, CoinTosser coinTosser, Console console) {
- this.board = board;
- this.coinTosser = coinTosser;
- this.console = console;
- }
-
- public Move executeMove(Move move) {
- if (!move.isJumpMove()) {
- board.executeMove(move);
- return move;
- }
- console.print(move.player() + " is making a jump move. Now " + move.player() + " may gamble.");
- console.print("If " + move.player() + " does not gamble, the jump move is executed normally.");
- console.print("If " + move.player() + " does gamble, a coin will be tossed.");
- console.print(
- "If the coin toss is "
- + HEADS
- + ", then the jump move will be executed, but "
- + move.player()
- + " gets another turn.");
- console.print(
- "If the coin toss is "
- + TAILS
- + ", then the jump move fails and the piece at "
- + move.start()
- + ", which would have been used for the jump move, is removed.");
- GambleChoice gambleChoice = null;
- do {
- console.print(
- move.player() + " type \"yes\" to gamble, \"no\" to execute the jump move normally");
- String userInput = console.getUserInput();
- try {
- gambleChoice = GambleChoice.valueOf(userInput.trim().toUpperCase());
- } catch (IllegalArgumentException e) {
- console.print("The input of " + move.player() + " was invalid. Input was: " + userInput);
- }
- } while (gambleChoice == null);
- if (gambleChoice == GambleChoice.NO) {
- board.executeMove(move);
- return move;
- }
- CoinTosser.Result tossResult = coinTosser.toss(board, move.player());
- JumpGambleResult jumpGambleResult =
- tossResult == HEADS ? JumpGambleResult.WON : JumpGambleResult.LOST;
- console.print("Coin toss resulted in " + tossResult + ", the gamble was: " + jumpGambleResult);
- Move newMove = move.withJumpGambleResult(jumpGambleResult);
- board.executeMove(newMove);
- return newMove;
- }
-
- private enum GambleChoice {
- @SuppressWarnings("unused")
- YES,
- NO
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/README.md b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/README.md
deleted file mode 100644
index f3a62aab..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# Checkers pattern examples
-
-These examples were taken from [Soco21-group8 checkersV3](https://github.com/soco21/soco21-group8/tree/main/assignment-3/checkersv3)
-with the permission of it's author [@BacLuc](https://github.com/BacLuc)
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/WinCondition.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/WinCondition.java
deleted file mode 100644
index 6b266558..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/WinCondition.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import ch.scs.jumpstart.pattern.examples.checkers.movevalidator.MoveValidator;
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Check if one player cannot move or has no pieces. This can be done in one go, as if one player
- * has no pieces, he cannot move. Use MoveValidators to check if any move is possible.
- */
-@SuppressWarnings("ClassCanBeRecord")
-public class WinCondition {
- private final List moveValidators;
-
- public WinCondition(List moveValidators) {
- this.moveValidators = moveValidators;
- }
-
- public boolean hasPlayerWon(Player player, Board board) {
- for (Row row : Row.values()) {
- for (Column col : Column.values()) {
- BoardCoordinates currentCoordinates = new BoardCoordinates(row, col);
- Optional pieceAt = board.getPieceAt(currentCoordinates);
- if (pieceAt.isEmpty()) {
- continue;
- }
- Piece piece = pieceAt.get();
- if (piece.owner() == player) {
- continue;
- }
- List possibleMoves =
- Move.generatePossibleMoves(currentCoordinates, piece.owner(), List.of(1, 2));
- if (possibleMoves.stream()
- .anyMatch(
- move ->
- moveValidators.stream()
- .allMatch(moveValidator -> moveValidator.validate(move, board)))) {
- return false;
- }
- }
- }
- return true;
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/BoardCoordinates.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/BoardCoordinates.java
deleted file mode 100644
index 3d2ccef7..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/BoardCoordinates.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom;
-
-public record BoardCoordinates(Row row, Column column) {
-
- public enum Row {
- ROW_1,
- ROW_2,
- ROW_3,
- ROW_4,
- ROW_5,
- ROW_6,
- ROW_7,
- ROW_8;
-
- public boolean isLastRow() {
- return this == ROW_8;
- }
-
- public boolean isFirstRow() {
- return this == ROW_1;
- }
-
- public int diffRow(Row row) {
- return this.ordinal() - row.ordinal();
- }
-
- public Row getRowBetween(Row row) {
- int indexBetween = (this.ordinal() + row.ordinal()) / 2;
- return values()[indexBetween];
- }
- }
-
- public enum Column {
- A,
- B,
- C,
- D,
- E,
- F,
- G,
- H;
-
- public int diffCol(Column column) {
- return this.ordinal() - column.ordinal();
- }
-
- public Column getColBetween(Column column) {
- int indexBetween = (this.ordinal() + column.ordinal()) / 2;
- return values()[indexBetween];
- }
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/BoardObserver.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/BoardObserver.java
deleted file mode 100644
index 73be190c..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/BoardObserver.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-
-public interface BoardObserver {
- void boardChanged(Board board);
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/JumpGambleResult.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/JumpGambleResult.java
deleted file mode 100644
index 2cb6afe8..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/JumpGambleResult.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom;
-
-public enum JumpGambleResult {
- NO_GAMBLE,
- WON,
- LOST
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/Move.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/Move.java
deleted file mode 100644
index 2f208134..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/Move.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.*;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-public record Move(
- Player player,
- BoardCoordinates start,
- BoardCoordinates end,
- JumpGambleResult jumpGambleResult) {
- public static Move parse(Player player, String string) {
- String replacedString = string.replace("X", "x").replace("[", "").replace("]", "");
- String[] startAndEnd = replacedString.split("x");
- if (startAndEnd.length < 2) {
- throw new IllegalArgumentException("invalid input");
- }
- BoardCoordinates start = parseBoardCoordinates(startAndEnd[0]);
- BoardCoordinates end = parseBoardCoordinates(startAndEnd[1]);
-
- return of(player, start, end);
- }
-
- public static Move of(Player player, BoardCoordinates start, BoardCoordinates end) {
- return new Move(player, start, end, JumpGambleResult.NO_GAMBLE);
- }
-
- public static List generatePossibleMoves(
- BoardCoordinates boardCoordinates, Player pieceOwner, List distances) {
- int rowIndex = boardCoordinates.row().ordinal();
- int colIndex = boardCoordinates.column().ordinal();
- List possibleJumpMoves = new ArrayList<>();
-
- for (Integer distance : distances) {
- of(pieceOwner, boardCoordinates, rowIndex + distance, colIndex + distance)
- .ifPresent(possibleJumpMoves::add);
- of(pieceOwner, boardCoordinates, rowIndex + distance, colIndex - distance)
- .ifPresent(possibleJumpMoves::add);
- of(pieceOwner, boardCoordinates, rowIndex - distance, colIndex + distance)
- .ifPresent(possibleJumpMoves::add);
- of(pieceOwner, boardCoordinates, rowIndex - distance, colIndex - distance)
- .ifPresent(possibleJumpMoves::add);
- }
- return possibleJumpMoves;
- }
-
- private static Optional of(
- Player player, BoardCoordinates start, int rowIndex, int colIndex) {
- Row[] rows = Row.values();
- Column[] columns = Column.values();
-
- if (rowIndex >= 0 && rowIndex < rows.length && colIndex >= 0 && colIndex < columns.length) {
- return Optional.of(
- of(player, start, new BoardCoordinates(rows[rowIndex], columns[colIndex])));
- }
- return Optional.empty();
- }
-
- private static BoardCoordinates parseBoardCoordinates(String s) {
- String[] columnAndRow = s.split("");
- if (columnAndRow.length != 2) {
- throw new IllegalArgumentException("invalid input");
- }
- String columnString = columnAndRow[0];
- Column column = Column.valueOf(columnString.toUpperCase());
-
- String rowString = columnAndRow[1];
- for (Row row : Row.values()) {
- int enumValue = row.ordinal() + 1;
- if (rowString.equals(String.valueOf(enumValue))) {
- return new BoardCoordinates(row, column);
- }
- }
- throw new IllegalArgumentException("invalid input");
- }
-
- public Optional getCoordinatesBetween() {
- int rowDiff = start.row().diffRow(end.row());
- int colDiff = start.column().diffCol(end.column());
-
- if (Math.abs(rowDiff) != 2 || Math.abs(colDiff) != 2) {
- return Optional.empty();
- }
-
- return Optional.of(
- new BoardCoordinates(
- start.row().getRowBetween(end.row()), start.column().getColBetween(end.column())));
- }
-
- public boolean isJumpMove() {
- int diffMoveColumn = start.column().diffCol(end.column());
- int diffMoveRow = start.row().diffRow(end.row());
- return Math.abs(diffMoveRow) == 2 && Math.abs(diffMoveColumn) == 2;
- }
-
- public Move withJumpGambleResult(JumpGambleResult jumpGambleResult) {
- if (jumpGambleResult != JumpGambleResult.NO_GAMBLE && !isJumpMove()) {
- throw new IllegalArgumentException(
- "cannot create move with JumpGambleResult"
- + jumpGambleResult
- + ", when the move is no jump move");
- }
- return new Move(player, start, end, jumpGambleResult);
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/Piece.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/Piece.java
deleted file mode 100644
index c2d3cb30..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/Piece.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom;
-
-public record Piece(Player owner, boolean isKing) {}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/Player.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/Player.java
deleted file mode 100644
index ff48a421..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/Player.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom;
-
-public enum Player {
- PLAYER_WHITE,
- PLAYER_RED
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/Board.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/Board.java
deleted file mode 100644
index 74d4949f..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/Board.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom.board;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.*;
-import ch.scs.jumpstart.pattern.examples.checkers.util.Tuple;
-import java.util.*;
-
-public class Board {
- private final List boardObservers = new ArrayList<>();
- private final Store store = new Store();
- private final List> executedCommands = new ArrayList<>();
-
- public void executeMove(Move move) {
- Command command = createCommand(move);
- command.execute();
- executedCommands.add(Tuple.of(move.player(), command));
- boardObservers.forEach(boardObserver -> boardObserver.boardChanged(this));
- }
-
- public Player undoLastTurn() throws NoPreviousMovesException {
- if (executedCommands.isEmpty()) {
- throw new NoPreviousMovesException();
- }
- Tuple lastCommandTuple = executedCommands.get(executedCommands.size() - 1);
- for (int i = executedCommands.size() - 1; i >= 0; i--) {
- Tuple currentCommandTuple = executedCommands.get(i);
- if (!currentCommandTuple.getKey().equals(lastCommandTuple.getKey())) {
- break;
- }
- currentCommandTuple.getValue().undo();
- executedCommands.remove(i);
- }
- boardObservers.forEach(boardObserver -> boardObserver.boardChanged(this));
- return lastCommandTuple.getKey();
- }
-
- public Optional getPieceAt(BoardCoordinates boardCoordinates) {
- return store.getPieceAt(boardCoordinates);
- }
-
- public void registerObserver(BoardObserver boardObserver) {
- boardObservers.add(boardObserver);
- }
-
- private Command createCommand(Move move) {
- Piece pieceAtStart = store.getPieceAt(move.start()).orElseThrow();
- Tuple start = Tuple.of(move.start(), pieceAtStart);
- if (move.jumpGambleResult() == JumpGambleResult.LOST) {
- return new JumpGambleLostMove(store, start);
- }
-
- boolean convertToKing = isConvertToKing(move);
- Piece pieceAtEnd = new Piece(move.player(), pieceAtStart.isKing() || convertToKing);
- Tuple end = Tuple.of(move.end(), pieceAtEnd);
- if (move.isJumpMove()) {
- Piece pieceBetween = move.getCoordinatesBetween().flatMap(store::getPieceAt).orElseThrow();
- return new JumpMove(
- store, start, Tuple.of(move.getCoordinatesBetween().orElseThrow(), pieceBetween), end);
- } else {
- return new SimpleMove(store, start, end);
- }
- }
-
- private boolean isConvertToKing(Move move) {
- if (move.player() == Player.PLAYER_WHITE) {
- return move.end().row().isLastRow();
- }
- return move.end().row().isFirstRow();
- }
-
- public static class NoPreviousMovesException extends Exception {
- private NoPreviousMovesException() {
- super();
- }
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/Command.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/Command.java
deleted file mode 100644
index e4ad021b..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/Command.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom.board;
-
-interface Command {
- void execute();
-
- void undo();
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/JumpGambleLostMove.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/JumpGambleLostMove.java
deleted file mode 100644
index 879fbd19..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/JumpGambleLostMove.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom.board;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.util.Tuple;
-
-@SuppressWarnings("ClassCanBeRecord")
-class JumpGambleLostMove implements Command {
- private final Store store;
- private final Tuple start;
-
- JumpGambleLostMove(Store store, Tuple start) {
- this.store = store;
- this.start = start;
- }
-
- @Override
- public void execute() {
- store.removePiece(start.getKey());
- }
-
- @Override
- public void undo() {
- store.addPiece(start.getKey(), start.getValue());
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/JumpMove.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/JumpMove.java
deleted file mode 100644
index aad2cc78..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/JumpMove.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom.board;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.util.Tuple;
-
-@SuppressWarnings("ClassCanBeRecord")
-public class JumpMove implements Command {
- private final Store store;
- private final Tuple start;
- private final Tuple pieceBetween;
- private final Tuple end;
-
- public JumpMove(
- Store store,
- Tuple start,
- Tuple pieceBetween,
- Tuple end) {
- this.store = store;
- this.start = start;
- this.pieceBetween = pieceBetween;
- this.end = end;
- }
-
- @Override
- public void execute() {
- store.removePiece(start.getKey());
- store.removePiece(pieceBetween.getKey());
- store.addPiece(end.getKey(), end.getValue());
- }
-
- @Override
- public void undo() {
- store.addPiece(start.getKey(), start.getValue());
- store.addPiece(pieceBetween.getKey(), pieceBetween.getValue());
- store.removePiece(end.getKey());
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/SimpleMove.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/SimpleMove.java
deleted file mode 100644
index caf6a7f9..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/SimpleMove.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom.board;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.util.Tuple;
-
-@SuppressWarnings("ClassCanBeRecord")
-public class SimpleMove implements Command {
- private final Store store;
- private final Tuple start;
- private final Tuple end;
-
- public SimpleMove(
- Store store, Tuple start, Tuple end) {
- this.store = store;
- this.start = start;
- this.end = end;
- }
-
- @Override
- public void execute() {
- store.removePiece(start.getKey());
- store.addPiece(end.getKey(), end.getValue());
- }
-
- @Override
- public void undo() {
- store.addPiece(start.getKey(), start.getValue());
- store.removePiece(end.getKey());
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/Store.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/Store.java
deleted file mode 100644
index 13622633..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/Store.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom.board;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.*;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-
-class Store {
- final Map> boardMatrix = new HashMap<>();
-
- Store() {
- boardMatrix.put(
- Row.ROW_1,
- Map.of(
- Column.A,
- new Piece(Player.PLAYER_WHITE, false),
- Column.C,
- new Piece(Player.PLAYER_WHITE, false),
- Column.E,
- new Piece(Player.PLAYER_WHITE, false),
- Column.G,
- new Piece(Player.PLAYER_WHITE, false)));
- boardMatrix.put(
- Row.ROW_3,
- Map.of(
- Column.A,
- new Piece(Player.PLAYER_WHITE, false),
- Column.C,
- new Piece(Player.PLAYER_WHITE, false),
- Column.E,
- new Piece(Player.PLAYER_WHITE, false),
- Column.G,
- new Piece(Player.PLAYER_WHITE, false)));
- boardMatrix.put(
- Row.ROW_2,
- Map.of(
- Column.B,
- new Piece(Player.PLAYER_WHITE, false),
- Column.D,
- new Piece(Player.PLAYER_WHITE, false),
- Column.F,
- new Piece(Player.PLAYER_WHITE, false),
- Column.H,
- new Piece(Player.PLAYER_WHITE, false)));
-
- boardMatrix.put(
- Row.ROW_8,
- Map.of(
- Column.B,
- new Piece(Player.PLAYER_RED, false),
- Column.D,
- new Piece(Player.PLAYER_RED, false),
- Column.F,
- new Piece(Player.PLAYER_RED, false),
- Column.H,
- new Piece(Player.PLAYER_RED, false)));
- boardMatrix.put(
- Row.ROW_6,
- Map.of(
- Column.B,
- new Piece(Player.PLAYER_RED, false),
- Column.D,
- new Piece(Player.PLAYER_RED, false),
- Column.F,
- new Piece(Player.PLAYER_RED, false),
- Column.H,
- new Piece(Player.PLAYER_RED, false)));
- boardMatrix.put(
- Row.ROW_7,
- Map.of(
- Column.A,
- new Piece(Player.PLAYER_RED, false),
- Column.C,
- new Piece(Player.PLAYER_RED, false),
- Column.E,
- new Piece(Player.PLAYER_RED, false),
- Column.G,
- new Piece(Player.PLAYER_RED, false)));
- }
-
- Optional getPieceAt(BoardCoordinates boardCoordinates) {
- Map columnOptionalMap = boardMatrix.get(boardCoordinates.row());
- if (columnOptionalMap == null) {
- return Optional.empty();
- }
- return Optional.ofNullable(columnOptionalMap.get(boardCoordinates.column()));
- }
-
- void removePiece(BoardCoordinates start) {
- boardMatrix.compute(
- start.row(),
- (row, columnPieceMap) -> {
- if (columnPieceMap == null) {
- return new HashMap<>();
- } else {
- Map columnPieceHashMap = new HashMap<>(columnPieceMap);
- columnPieceHashMap.remove(start.column());
- return columnPieceHashMap;
- }
- });
- }
-
- void addPiece(BoardCoordinates boardCoordinates, Piece piece) {
- boardMatrix.compute(
- boardCoordinates.row(),
- (row, columnPieceMap) -> {
- if (columnPieceMap == null) {
- return Map.of(boardCoordinates.column(), piece);
- } else {
- Map columnPieceHashMap = new HashMap<>(columnPieceMap);
- columnPieceHashMap.put(boardCoordinates.column(), piece);
- return columnPieceHashMap;
- }
- });
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveIsDiagonal.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveIsDiagonal.java
deleted file mode 100644
index bdbae8e6..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveIsDiagonal.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-
-public class MoveIsDiagonal implements MoveValidator {
- @Override
- public boolean validate(Move move, Board board) {
- BoardCoordinates start = move.start();
- BoardCoordinates end = move.end();
- boolean bothDirectionsChange = start.row() != end.row() && start.column() != end.column();
-
- int rowDiff = Math.abs(start.row().ordinal() - end.row().ordinal());
- int colDiff = Math.abs(start.column().ordinal() - end.column().ordinal());
- return bothDirectionsChange && rowDiff == colDiff;
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveIsForwardIfNotKing.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveIsForwardIfNotKing.java
deleted file mode 100644
index 68c790b3..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveIsForwardIfNotKing.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.Optional;
-
-public class MoveIsForwardIfNotKing implements MoveValidator {
- @Override
- public boolean validate(Move move, Board board) {
- Optional pieceAt = board.getPieceAt(move.start());
- if (pieceAt.isEmpty()) {
- return true;
- }
- Piece piece = pieceAt.get();
- if (piece.isKing()) {
- return true;
- }
- if (piece.owner().equals(Player.PLAYER_WHITE)) {
- return move.start().row().ordinal() < move.end().row().ordinal();
-
- } else if (piece.owner().equals(Player.PLAYER_RED)) {
- return move.start().row().ordinal() > move.end().row().ordinal();
- }
- return false;
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveLength.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveLength.java
deleted file mode 100644
index 3380c0fc..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveLength.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-
-public class MoveLength implements MoveValidator {
- @Override
- public boolean validate(Move move, Board board) {
- BoardCoordinates start = move.start();
- BoardCoordinates end = move.end();
- int diffMoveColumn = start.column().diffCol(end.column());
- int diffMoveRow = start.row().diffRow(end.row());
- if (diffMoveColumn == 0 && diffMoveRow == 0) {
- return false;
- }
- return Math.abs(diffMoveRow) <= 2 && Math.abs(diffMoveColumn) <= 2;
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveValidator.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveValidator.java
deleted file mode 100644
index 42dc6b24..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveValidator.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-
-public interface MoveValidator {
- boolean validate(Move move, Board board);
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/NoOtherMoveToJumpPossible.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/NoOtherMoveToJumpPossible.java
deleted file mode 100644
index 6db87cf1..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/NoOtherMoveToJumpPossible.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.*;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.List;
-import java.util.Optional;
-
-public class NoOtherMoveToJumpPossible implements MoveValidator {
-
- private final List jumpValidators;
-
- public NoOtherMoveToJumpPossible(
- MoveIsForwardIfNotKing moveIsForwardIfNotKings,
- OpponentPieceBetweenJump opponentPieceBetweenJump,
- TargetFieldEmpty targetFieldEmpty) {
- jumpValidators = List.of(moveIsForwardIfNotKings, opponentPieceBetweenJump, targetFieldEmpty);
- }
-
- @Override
- public boolean validate(Move move, Board board) {
- Row[] rows = Row.values();
- Column[] columns = Column.values();
- if (move.isJumpMove()) {
- return true;
- }
- for (Row row : rows) {
- for (Column col : columns) {
- BoardCoordinates currentCoordinates = new BoardCoordinates(row, col);
- Optional pieceAt = board.getPieceAt(currentCoordinates);
- if (pieceAt.isEmpty()) {
- continue;
- }
- if (pieceAt.get().owner() != move.player()) {
- continue;
- }
- if (jumpMovePossibleFrom(currentCoordinates, board)) {
- return false;
- }
- }
- }
- return true;
- }
-
- public boolean jumpMovePossibleFrom(BoardCoordinates boardCoordinates, Board board) {
- Optional pieceAt = board.getPieceAt(boardCoordinates);
- if (pieceAt.isEmpty()) {
- return false;
- }
- Player pieceOwner = pieceAt.get().owner();
-
- List possibleJumpMoves =
- Move.generatePossibleMoves(boardCoordinates, pieceOwner, List.of(2));
-
- return possibleJumpMoves.stream()
- .anyMatch(
- move ->
- jumpValidators.stream()
- .allMatch(moveValidator -> moveValidator.validate(move, board)));
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/OpponentPieceBetweenJump.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/OpponentPieceBetweenJump.java
deleted file mode 100644
index 99e77f82..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/OpponentPieceBetweenJump.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.Optional;
-
-public class OpponentPieceBetweenJump implements MoveValidator {
- @Override
- public boolean validate(Move move, Board board) {
- Optional coordinatesBetween = move.getCoordinatesBetween();
- if (coordinatesBetween.isEmpty()) {
- return true;
- }
- Optional pieceBetweenJump = board.getPieceAt(coordinatesBetween.get());
- if (pieceBetweenJump.isEmpty()) {
- return false;
- }
- return pieceBetweenJump.get().owner() != move.player();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/StartPieceValid.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/StartPieceValid.java
deleted file mode 100644
index 87847a1c..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/StartPieceValid.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.Optional;
-
-public class StartPieceValid implements MoveValidator {
- @Override
- public boolean validate(Move move, Board board) {
- BoardCoordinates start = move.start();
- Optional startPiece = board.getPieceAt(start);
- return startPiece.filter(piece -> piece.owner() == move.player()).isPresent();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/TargetFieldEmpty.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/TargetFieldEmpty.java
deleted file mode 100644
index ae41a8d9..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/TargetFieldEmpty.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.Optional;
-
-public class TargetFieldEmpty implements MoveValidator {
- @Override
- public boolean validate(Move move, Board board) {
- Optional pieceAt = board.getPieceAt(move.end());
- return pieceAt.isEmpty();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/BoardPrinter.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/BoardPrinter.java
deleted file mode 100644
index e8742c04..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/BoardPrinter.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.util;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.*;
-
-public class BoardPrinter {
- private final Console console;
-
- public BoardPrinter(Console console) {
- this.console = console;
- }
-
- @SuppressWarnings("PMD.CognitiveComplexity")
- public void printBoard(Board board) {
- StringBuilder header = new StringBuilder();
- header.append(" ");
- for (Column col : Column.values()) {
- header.append(col.name().toLowerCase(Locale.ROOT)).append(" ");
- }
- console.print(header.toString());
- console.print(" +-------------------------------------------------+");
-
- List rows = Arrays.asList(Row.values());
- Collections.reverse(rows);
- for (Row row : rows) {
- StringBuilder rowString = new StringBuilder((row.ordinal() + 1) + " |");
- for (Column col : Column.values()) {
- Optional pieceAt = board.getPieceAt(new BoardCoordinates(row, col));
- rowString.append(" ");
- if (pieceAt.isEmpty()) {
- rowString.append("[ ]");
- } else {
- Piece piece = pieceAt.get();
- rowString.append("[");
- if (piece.owner() == Player.PLAYER_WHITE) {
- rowString.append("W");
- } else {
- rowString.append("R");
- }
- rowString.append("_");
- if (piece.isKing()) {
- rowString.append("K");
- } else {
- rowString.append("P");
- }
- rowString.append("]");
- }
- }
- console.print(rowString + " | " + (row.ordinal() + 1));
- }
-
- console.print(" +-------------------------------------------------+");
- console.print(header.toString());
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/CoinTosser.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/CoinTosser.java
deleted file mode 100644
index 2eadbb6c..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/CoinTosser.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.util;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.Map;
-import java.util.Random;
-
-public class CoinTosser {
- private final PointsCalculator pointsCalculator;
- private final Random random;
-
- public CoinTosser(PointsCalculator pointsCalculator) {
- this(pointsCalculator, new Random());
- }
-
- CoinTosser(PointsCalculator pointsCalculator, Random random) {
- this.pointsCalculator = pointsCalculator;
- this.random = random;
- }
-
- public Result toss(Board board, Player player) {
- Map playerPointsMap = pointsCalculator.calculatePoints(board);
- int totalPoints =
- playerPointsMap.get(Player.PLAYER_RED) + playerPointsMap.get(Player.PLAYER_WHITE);
- float winChance = 1 - playerPointsMap.get(player).floatValue() / totalPoints;
- return random.nextFloat() <= winChance ? Result.HEADS : Result.TAILS;
- }
-
- public enum Result {
- HEADS,
- TAILS
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/Console.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/Console.java
deleted file mode 100644
index a047b3e2..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/Console.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.util;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-
-public class Console {
- private static final Console instance = new Console();
-
- public static Console getInstance() {
- return instance;
- }
-
- private Console() {}
-
- @SuppressWarnings("PMD.SystemPrintln")
- public void print(String string) {
- System.out.println(string);
- }
-
- public String getUserInput() {
- BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
-
- try {
- return reader.readLine();
- } catch (IOException e) {
- return "";
- }
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/PointsCalculator.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/PointsCalculator.java
deleted file mode 100644
index 260f71f8..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/PointsCalculator.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.util;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.Player.PLAYER_RED;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.Player.PLAYER_WHITE;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-
-public class PointsCalculator {
-
- @SuppressWarnings("PMD.CognitiveComplexity")
- public Map calculatePoints(Board board) {
- int pointsPlayerWhite = 0;
- int pointsPlayerRed = 0;
- BoardCoordinates.Row[] rows = BoardCoordinates.Row.values();
- BoardCoordinates.Column[] columns = BoardCoordinates.Column.values();
- for (BoardCoordinates.Row row : rows) {
- for (BoardCoordinates.Column col : columns) {
- BoardCoordinates currentCoordinates = new BoardCoordinates(row, col);
- Optional pieceAt = board.getPieceAt(currentCoordinates);
- if (pieceAt.isPresent()) {
- if (pieceAt.get().owner() == PLAYER_WHITE) {
- if (pieceAt.get().isKing()) {
- pointsPlayerWhite += 2;
- } else {
- pointsPlayerWhite += 1;
- }
- } else {
- if (pieceAt.get().isKing()) {
- pointsPlayerRed += 2;
- } else {
- pointsPlayerRed += 1;
- }
- }
- }
- }
- }
- Map pointsOnBoard = new HashMap<>();
- pointsOnBoard.put(PLAYER_WHITE, pointsPlayerWhite);
- pointsOnBoard.put(PLAYER_RED, pointsPlayerRed);
-
- return pointsOnBoard;
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/Tuple.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/Tuple.java
deleted file mode 100644
index 11c1fb3e..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/checkers/util/Tuple.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.util;
-
-import java.util.AbstractMap;
-
-public class Tuple extends AbstractMap.SimpleImmutableEntry {
- public static Tuple of(K key, V value) {
- return new Tuple<>(key, value);
- }
-
- private Tuple(K key, V value) {
- super(key, value);
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/Button.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/Button.java
deleted file mode 100644
index 4ba1e8d0..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/Button.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.gui;
-
-public interface Button {}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/ButtonFactory.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/ButtonFactory.java
deleted file mode 100644
index 63da870a..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/ButtonFactory.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.gui;
-
-@SuppressWarnings("unused")
-public interface ButtonFactory {
- Button createBackButton();
-
- Button createForwardButton();
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/FlatButton.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/FlatButton.java
deleted file mode 100644
index 64580b89..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/FlatButton.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.gui;
-
-public record FlatButton(String buttonName) implements Button {}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/FlatButtonFactory.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/FlatButtonFactory.java
deleted file mode 100644
index d1cf12bc..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/FlatButtonFactory.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.gui;
-
-@SuppressWarnings("unused")
-public class FlatButtonFactory implements ButtonFactory {
- @Override
- public Button createBackButton() {
- return new FlatButton("back");
- }
-
- @Override
- public Button createForwardButton() {
- return new FlatButton("forward");
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/RoundedButton.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/RoundedButton.java
deleted file mode 100644
index 00751dec..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/RoundedButton.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.gui;
-
-public record RoundedButton(String buttonName) implements Button {}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/RoundedButtonFactory.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/RoundedButtonFactory.java
deleted file mode 100644
index cc33a6d0..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/gui/RoundedButtonFactory.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.gui;
-
-@SuppressWarnings("unused")
-public class RoundedButtonFactory implements ButtonFactory {
- @Override
- public Button createBackButton() {
- return new RoundedButton("back");
- }
-
- @Override
- public Button createForwardButton() {
- return new RoundedButton("forward");
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/ConfigurationOrganisationSupplier.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/ConfigurationOrganisationSupplier.java
deleted file mode 100644
index bad0ec56..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/ConfigurationOrganisationSupplier.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.organisation;
-
-import ch.scs.jumpstart.pattern.examples.organisation.infra.Configuration;
-import ch.scs.jumpstart.pattern.examples.organisation.infra.OrganisationRepository;
-import ch.scs.jumpstart.pattern.examples.organisation.infra.domain.Organisation;
-
-@SuppressWarnings("unused")
-public class ConfigurationOrganisationSupplier implements OrganisationSupplier {
-
- private final Configuration config;
- private final OrganisationRepository organisationRepository;
-
- public ConfigurationOrganisationSupplier(
- Configuration config, OrganisationRepository organisationRepository) {
- this.config = config;
- this.organisationRepository = organisationRepository;
- }
-
- @Override
- public Organisation getOrganisation() {
- return config
- .getOrganisationId()
- .map(organisationRepository::getById)
- .orElseThrow(() -> new RuntimeException("Organisation ID not found"));
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/LocationOrganisationProvider.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/LocationOrganisationProvider.java
deleted file mode 100644
index 1e8f8f15..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/LocationOrganisationProvider.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.organisation;
-
-import ch.scs.jumpstart.pattern.examples.organisation.infra.Configuration;
-import ch.scs.jumpstart.pattern.examples.organisation.infra.LocationRepository;
-import ch.scs.jumpstart.pattern.examples.organisation.infra.domain.Location;
-import ch.scs.jumpstart.pattern.examples.organisation.infra.domain.Organisation;
-
-@SuppressWarnings("unused")
-public class LocationOrganisationProvider implements OrganisationSupplier {
-
- private final Configuration config;
- private final LocationRepository locationRepository;
-
- public LocationOrganisationProvider(Configuration config, LocationRepository locationRepository) {
- this.config = config;
- this.locationRepository = locationRepository;
- }
-
- @Override
- public Organisation getOrganisation() {
- return config
- .getOrganisationId()
- .map(locationRepository::getById)
- .map(Location::organisation)
- .orElseThrow(() -> new RuntimeException("Location not found"));
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/OrganisationSupplier.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/OrganisationSupplier.java
deleted file mode 100644
index bbea8d3e..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/OrganisationSupplier.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.organisation;
-
-import ch.scs.jumpstart.pattern.examples.organisation.infra.domain.Organisation;
-
-@SuppressWarnings("unused")
-public interface OrganisationSupplier {
- Organisation getOrganisation();
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/Configuration.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/Configuration.java
deleted file mode 100644
index 060bb45e..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/Configuration.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.organisation.infra;
-
-import java.util.Optional;
-
-public class Configuration {
- public Optional getOrganisationId() {
- // as example, this would read it from some kind of configuration.
- return Optional.of(0L);
- }
-
- public Optional getLocationId() {
- return Optional.of(0L);
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/LocationRepository.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/LocationRepository.java
deleted file mode 100644
index e32db2d1..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/LocationRepository.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.organisation.infra;
-
-import ch.scs.jumpstart.pattern.examples.organisation.infra.domain.Location;
-
-public interface LocationRepository {
- Location getById(long id);
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/OrganisationRepository.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/OrganisationRepository.java
deleted file mode 100644
index 06d2300f..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/OrganisationRepository.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.organisation.infra;
-
-import ch.scs.jumpstart.pattern.examples.organisation.infra.domain.Organisation;
-
-public interface OrganisationRepository {
- Organisation getById(long id);
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/domain/Location.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/domain/Location.java
deleted file mode 100644
index 1f3e9db8..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/domain/Location.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.organisation.infra.domain;
-
-public record Location(long id, String name, Organisation organisation) {}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/domain/Organisation.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/domain/Organisation.java
deleted file mode 100644
index d9f75b36..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/organisation/infra/domain/Organisation.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.organisation.infra.domain;
-
-public record Organisation(long id, String name) {}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/receipts/Customer1ReceiptFactory.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/receipts/Customer1ReceiptFactory.java
deleted file mode 100644
index 930e22bd..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/receipts/Customer1ReceiptFactory.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.receipts;
-
-// details omitted
-public class Customer1ReceiptFactory implements ReceiptFactory {
- @Override
- public Receipt createEftReceipt() {
- // details omitted
- return new Receipt();
- }
-
- @Override
- public Receipt createRefundReceipt() {
- throw new UnsupportedOperationException(
- "%s does not support refund".formatted(getClass().getSimpleName()));
- }
-
- @Override
- public Receipt createContactlessReceipt() {
- // details omitted
- return new Receipt();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/receipts/Receipt.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/receipts/Receipt.java
deleted file mode 100644
index 76dc0f4b..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/receipts/Receipt.java
+++ /dev/null
@@ -1,3 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.receipts;
-
-public class Receipt {}
diff --git a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/receipts/ReceiptFactory.java b/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/receipts/ReceiptFactory.java
deleted file mode 100644
index 6d3e9f4b..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/receipts/ReceiptFactory.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.receipts;
-
-@SuppressWarnings("unused")
-public interface ReceiptFactory {
- Receipt createEftReceipt();
-
- Receipt createRefundReceipt();
-
- Receipt createContactlessReceipt();
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/BrightnessControlTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/BrightnessControlTest.java
deleted file mode 100644
index 7bd55afb..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/BrightnessControlTest.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package ch.scs.jumpstart.pattern.examples;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.IntStream;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-class BrightnessControlTest {
- public static final int INPUT_MAX = 4015;
- public static final int OUTPUT_MAX = 1000;
- public static final int OUTPUT_MIN = 0;
- public static final int OUTPUT_CHANGE_MAX = 10;
-
- @TempDir private Path tempDir;
-
- private Path input;
-
- @BeforeEach
- void setup() {
- input = tempDir.resolve("input");
- }
-
- @Test
- void climbs_to_target_value_in_steps() throws IOException {
- var brightnessControl =
- new BrightnessControl(
- false, INPUT_MAX, input.toString(), OUTPUT_MIN, OUTPUT_MAX, OUTPUT_CHANGE_MAX);
- Files.writeString(input, "0");
- brightnessControl.update();
-
- Files.writeString(input, "60");
-
- var outputValues = new ArrayList<>();
- for (int __ : IntStream.range(0, 10).toArray()) {
- int update = brightnessControl.update();
- outputValues.add(update);
- }
-
- assertThat(outputValues).isEqualTo(List.of(10, 20, 30, 40, 50, 52, 52, 52, 52, 52));
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/GameLogicTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/GameLogicTest.java
deleted file mode 100644
index 41d6d15a..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/GameLogicTest.java
+++ /dev/null
@@ -1,409 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers;
-
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Mockito.*;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser;
-import ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser.Result;
-import ch.scs.jumpstart.pattern.examples.checkers.util.Console;
-import java.util.List;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.mockito.InOrder;
-
-class GameLogicTest {
-
- private CoinTosser coinTosser;
- private GameLogic gameLogic;
- private Console console;
-
- @BeforeEach
- void setup() {
- console = mock(Console.class);
- doCallRealMethod().when(console).print(notNull());
- coinTosser = mock(CoinTosser.class);
- gameLogic = Main.createGameLogic(console, coinTosser);
- }
-
- @Test
- void end_game_with_winner() {
- // numbers were taken from: http://www.quadibloc.com/other/bo1211.htm
- when(console.getUserInput())
- // RED
- .thenReturn(fromNumbers(11, 15))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(23, 19))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(8, 11))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(22, 17))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(11, 16))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(24, 20))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(16, 23))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(27, 18))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(18, 11))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(7, 16))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(20, 11))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(3, 7))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(28, 24))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(7, 16))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(24, 20))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(16, 19))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(25, 22))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(4, 8))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(29, 25))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(9, 14))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(22, 18))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(14, 23))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(17, 14))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(10, 17))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(21, 14))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(2, 7))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(31, 27))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(6, 10))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(27, 18))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(10, 17))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(25, 21))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(1, 6))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(21, 14))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(6, 10))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(30, 25))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(10, 17))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(25, 21))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(19, 23))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(26, 19))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(17, 22))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(19, 15))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(22, 26))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(18, 14))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(26, 31))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(15, 10))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(5, 9))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(10, 3))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(9, 18))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(21, 17))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(18, 22))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(17, 14))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(22, 26))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(20, 16))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(12, 19))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(3, 12))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(26, 30))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(12, 16))
- .thenReturn("no")
-
- // Added moves that white wins
- // RED
- .thenReturn(fromNumbers(30, 26))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(16, 23))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(23, 30))
- .thenReturn("no")
- // RED
- .thenReturn(fromNumbers(31, 27))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(32, 23))
- .thenReturn("no");
-
- gameLogic.run();
-
- verify(console).print("Congratulations, player " + Player.PLAYER_WHITE + " has won");
- }
-
- @Test
- void only_allow_multiple_jump_with_same_piece() {
- // numbers were taken from: http://www.quadibloc.com/other/bo1211.htm
- when(console.getUserInput())
- // RED
- .thenReturn(fromNumbers(11, 15))
- // WHITE
- .thenReturn(fromNumbers(23, 19))
- // RED
- .thenReturn(fromNumbers(8, 11))
- // WHITE
- .thenReturn(fromNumbers(22, 17))
- // RED
- .thenReturn(fromNumbers(11, 16))
- // WHITE
- .thenReturn(fromNumbers(24, 20))
- // RED
- .thenReturn(fromNumbers(16, 23))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(27, 18))
- .thenReturn("no")
- // WHITE
- .thenReturn(fromNumbers(26, 11))
- .thenThrow(RuntimeException.class);
-
- Assertions.assertThrows(RuntimeException.class, gameLogic::run);
-
- verify(console).print("For a multiple jump move, the same piece has to be used. Try again");
- }
-
- @Test
- void let_same_player_play_again_if_jumpgamble_is_won() {
- when(coinTosser.toss(notNull(), notNull())).thenReturn(Result.HEADS);
- // numbers were taken from: http://www.quadibloc.com/other/bo1211.htm
- when(console.getUserInput())
- // RED
- .thenReturn(fromNumbers(11, 15))
- // WHITE
- .thenReturn(fromNumbers(23, 19))
- // RED
- .thenReturn(fromNumbers(8, 11))
- // WHITE
- .thenReturn(fromNumbers(22, 17))
- // RED
- .thenReturn(fromNumbers(11, 16))
- // WHITE
- .thenReturn(fromNumbers(24, 20))
- // RED
- .thenReturn(fromNumbers(16, 23))
- .thenReturn("yes")
- // RED
- .thenThrow(RuntimeException.class);
-
- Assertions.assertThrows(RuntimeException.class, gameLogic::run);
-
- verify(console).print("The gamble has been won, PLAYER_RED can play again.");
- }
-
- @Test
- void switch_player_if_jumpgamble_is_lost() {
- when(coinTosser.toss(notNull(), notNull())).thenReturn(Result.TAILS);
- InOrder inOrder = inOrder(console);
- // numbers were taken from: http://www.quadibloc.com/other/bo1211.htm
- when(console.getUserInput())
- // RED
- .thenReturn(fromNumbers(11, 15))
- // WHITE
- .thenReturn(fromNumbers(23, 19))
- // RED
- .thenReturn(fromNumbers(8, 11))
- // WHITE
- .thenReturn(fromNumbers(22, 17))
- // RED
- .thenReturn(fromNumbers(11, 16))
- // WHITE
- .thenReturn(fromNumbers(24, 20))
- // RED
- .thenReturn(fromNumbers(16, 23))
- .thenReturn("yes")
- // RED
- .thenThrow(RuntimeException.class);
-
- Assertions.assertThrows(RuntimeException.class, gameLogic::run);
-
- inOrder.verify(console).print("Coin toss resulted in TAILS, the gamble was: LOST");
- inOrder.verify(console).print("5 | [ ] [ ] [ ] [ ] [R_P] [ ] [ ] [ ] | 5");
- }
-
- @Test
- void show_error_message_if_undo_is_done_at_start_of_the_game() {
- when(console.getUserInput())
- // RED
- .thenReturn("undo")
- .thenThrow(RuntimeException.class);
-
- Assertions.assertThrows(RuntimeException.class, gameLogic::run);
-
- verify(console).print("There were no previous moves to undo. Please make a move.");
- }
-
- @Test
- void undo_previous_turn_if_undo_was_used_at_start_of_turn() {
- InOrder inOrder = inOrder(console);
- when(console.getUserInput())
- // RED
- .thenReturn(fromNumbers(11, 15))
- // WHITE
- .thenReturn(fromNumbers(23, 19))
- // RED
- .thenReturn("undo")
- .thenThrow(RuntimeException.class);
-
- Assertions.assertThrows(RuntimeException.class, gameLogic::run);
-
- inOrder.verify(console).print("4 | [ ] [ ] [ ] [ ] [ ] [W_P] [ ] [ ] | 4");
- inOrder.verify(console).print("3 | [W_P] [ ] [W_P] [ ] [ ] [ ] [W_P] [ ] | 3");
- inOrder
- .verify(console)
- .print(
- "PLAYER_RED, make your move. Or type 'undo' to go back to the start of the turn of PLAYER_WHITE");
- inOrder.verify(console).print("4 | [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] | 4");
- inOrder.verify(console).print("3 | [W_P] [ ] [W_P] [ ] [W_P] [ ] [W_P] [ ] | 3");
- }
-
- @Test
- void undo_current_move_if_player_inputs_undo_during_turn() {
- when(coinTosser.toss(notNull(), notNull())).thenReturn(Result.HEADS);
- InOrder inOrder = inOrder(console);
- // numbers were taken from: http://www.quadibloc.com/other/bo1211.htm
- when(console.getUserInput())
- // RED
- .thenReturn(fromNumbers(11, 15))
- // WHITE
- .thenReturn(fromNumbers(23, 19))
- // RED
- .thenReturn(fromNumbers(8, 11))
- // WHITE
- .thenReturn(fromNumbers(22, 17))
- // RED
- .thenReturn(fromNumbers(11, 16))
- // WHITE
- .thenReturn(fromNumbers(24, 20))
- // RED
- .thenReturn(fromNumbers(16, 23))
- .thenReturn("yes")
- // RED
- .thenReturn("undo")
- .thenThrow(RuntimeException.class);
-
- Assertions.assertThrows(RuntimeException.class, gameLogic::run);
-
- // start of move
- inOrder.verify(console).print("5 | [ ] [ ] [ ] [ ] [R_P] [ ] [R_P] [ ] | 5");
- inOrder.verify(console).print("4 | [ ] [W_P] [ ] [ ] [ ] [W_P] [ ] [W_P] | 4");
- inOrder.verify(console).print("3 | [W_P] [ ] [ ] [ ] [ ] [ ] [ ] [ ] | 3");
- // jump move
- inOrder.verify(console).print("5 | [ ] [ ] [ ] [ ] [R_P] [ ] [ ] [ ] | 5");
- inOrder.verify(console).print("4 | [ ] [W_P] [ ] [ ] [ ] [ ] [ ] [W_P] | 4");
- inOrder.verify(console).print("3 | [W_P] [ ] [ ] [ ] [R_P] [ ] [ ] [ ] | 3");
- // undo
- inOrder
- .verify(console)
- .print("PLAYER_RED, make your move. Or type 'undo' to go back to the start of your turn.");
- // board back at same state as start
- inOrder.verify(console).print("5 | [ ] [ ] [ ] [ ] [R_P] [ ] [R_P] [ ] | 5");
- inOrder.verify(console).print("4 | [ ] [W_P] [ ] [ ] [ ] [W_P] [ ] [W_P] | 4");
- inOrder.verify(console).print("3 | [W_P] [ ] [ ] [ ] [ ] [ ] [ ] [ ] | 3");
- }
-
- private static String fromNumbers(int start, int end) {
- List numberCoordinatesMap =
- List.of(
- "B8", "D8", "F8", "H8", "A7", "C7", "E7", "G7", "B6", "D6", "F6", "H6", "A5", "C5",
- "E5", "G5", "B4", "D4", "F4", "H4", "A3", "C3", "E3", "G3", "B2", "D2", "F2", "H2",
- "A1", "C1", "E1", "G1");
- return String.format(
- "%sx%s", numberCoordinatesMap.get(start - 1), numberCoordinatesMap.get(end - 1));
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/MoveExecutorTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/MoveExecutorTest.java
deleted file mode 100644
index 8bde14da..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/MoveExecutorTest.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row.*;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.*;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.JumpGambleResult;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser;
-import ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser.Result;
-import ch.scs.jumpstart.pattern.examples.checkers.util.Console;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class MoveExecutorTest {
-
- private static final Move NORMAL_MOVE =
- Move.of(Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
- private static final Move JUMP_MOVE =
- Move.of(Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_3, C));
- private static final Move JUMP_WITH_WON_GAMBLE_RESULT =
- JUMP_MOVE.withJumpGambleResult(JumpGambleResult.WON);
- private static final Move JUMP_WITH_LOST_GAMBLE_RESULT =
- JUMP_MOVE.withJumpGambleResult(JumpGambleResult.LOST);
- private CoinTosser coinTosser;
- private Board board;
- private MoveExecutor moveExecutor;
- private Console console;
-
- @BeforeEach
- void setup() {
- coinTosser = mock(CoinTosser.class);
- board = mock(Board.class);
- console = mock(Console.class);
- moveExecutor = new MoveExecutor(board, coinTosser, console);
- }
-
- @Test
- void execute_move_normally_if_no_jump_move() {
- Move move = NORMAL_MOVE;
-
- Move executedMove = moveExecutor.executeMove(move);
-
- verify(board).executeMove(same(move));
- assertThat(executedMove).isSameAs(move);
- }
-
- @Test
- void ask_player_if_he_wants_to_gamble_for_jump_move() {
- when(console.getUserInput()).thenThrow(RuntimeException.class);
-
- Assertions.assertThrows(RuntimeException.class, () -> moveExecutor.executeMove(JUMP_MOVE));
-
- verify(console).getUserInput();
- }
-
- @Test
- void ask_player_if_he_wants_to_gamble_until_he_types_yes() {
- when(console.getUserInput()).thenReturn("blabla").thenReturn("invalid").thenReturn("yes");
-
- moveExecutor.executeMove(JUMP_MOVE);
-
- verify(console, times(3)).getUserInput();
- }
-
- @Test
- void ask_player_if_he_wants_to_gamble_until_he_types_no() {
- when(console.getUserInput()).thenReturn("blabla").thenReturn("invalid").thenReturn("no");
-
- moveExecutor.executeMove(JUMP_MOVE);
-
- verify(console, times(3)).getUserInput();
- }
-
- @Test
- void execute_move_normally_if_player_types_no() {
- when(console.getUserInput()).thenReturn("no");
-
- Move executedMove = moveExecutor.executeMove(JUMP_MOVE);
-
- verify(board).executeMove(same(JUMP_MOVE));
- assertThat(executedMove).isSameAs(JUMP_MOVE);
- }
-
- @Test
- void execute_gamble_win_move_if_player_types_yes_and_wins() {
- when(console.getUserInput()).thenReturn("yes");
- when(coinTosser.toss(notNull(), notNull())).thenReturn(Result.HEADS);
-
- Move executedMove = moveExecutor.executeMove(JUMP_MOVE);
-
- verify(board).executeMove(eq(JUMP_WITH_WON_GAMBLE_RESULT));
- assertThat(executedMove).isEqualTo(JUMP_WITH_WON_GAMBLE_RESULT);
- }
-
- @Test
- void execute_gamble_lost_move_if_player_types_yes_and_loses() {
- when(console.getUserInput()).thenReturn("yes");
- when(coinTosser.toss(notNull(), notNull())).thenReturn(Result.TAILS);
-
- Move executedMove = moveExecutor.executeMove(JUMP_MOVE);
-
- verify(board).executeMove(eq(JUMP_WITH_LOST_GAMBLE_RESULT));
- assertThat(executedMove).isEqualTo(JUMP_WITH_LOST_GAMBLE_RESULT);
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/WinConditionTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/WinConditionTest.java
deleted file mode 100644
index 150b532c..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/WinConditionTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import ch.scs.jumpstart.pattern.examples.checkers.movevalidator.MoveValidator;
-import java.util.List;
-import java.util.Optional;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class WinConditionTest {
-
- private static final Piece WHITE_PAWN = new Piece(Player.PLAYER_WHITE, false);
- private static final Piece RED_PAWN = new Piece(Player.PLAYER_RED, false);
- private MoveValidator moveValidator1;
- private MoveValidator moveValidator2;
- private WinCondition winCondition;
- private Board board;
-
- @BeforeEach
- void setup() {
- moveValidator1 = mock(MoveValidator.class);
- moveValidator2 = mock(MoveValidator.class);
- winCondition = new WinCondition(List.of(moveValidator1, moveValidator2));
-
- board = mock(Board.class);
- }
-
- @Test
- void player_has_won_if_other_player_has_no_pieces() {
- when(board.getPieceAt(notNull())).thenReturn(Optional.empty());
- when(board.getPieceAt(new BoardCoordinates(Row.ROW_4, Column.E)))
- .thenReturn(Optional.of(RED_PAWN));
-
- assertThat(winCondition.hasPlayerWon(Player.PLAYER_RED, board)).isTrue();
- }
-
- @Test
- void player_has_not_won_if_other_player_can_move_a_piece() {
- when(board.getPieceAt(new BoardCoordinates(Row.ROW_4, Column.E)))
- .thenReturn(Optional.of(WHITE_PAWN));
- when(moveValidator1.validate(notNull(), notNull())).thenReturn(true);
- when(moveValidator2.validate(notNull(), notNull())).thenReturn(true);
-
- assertThat(winCondition.hasPlayerWon(Player.PLAYER_RED, board)).isFalse();
- }
-
- @Test
- void player_has_won_if_other_player_has_a_piece_but_cannot_move_it() {
- when(board.getPieceAt(new BoardCoordinates(Row.ROW_4, Column.E)))
- .thenReturn(Optional.of(WHITE_PAWN));
- when(moveValidator1.validate(notNull(), notNull())).thenReturn(false);
- when(moveValidator2.validate(notNull(), notNull())).thenReturn(true);
-
- assertThat(winCondition.hasPlayerWon(Player.PLAYER_RED, board)).isTrue();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/dom/BoardTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/dom/BoardTest.java
deleted file mode 100644
index 3d07b8c8..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/dom/BoardTest.java
+++ /dev/null
@@ -1,180 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom;
-
-import static java.util.Optional.empty;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.List;
-import java.util.Optional;
-import org.junit.jupiter.api.Test;
-
-class BoardTest {
- @Test
- void move_a_piece_from_a_to_b() {
- Board board = new Board();
- Move move =
- Move.of(
- Player.PLAYER_WHITE,
- new BoardCoordinates(Row.ROW_3, Column.A),
- new BoardCoordinates(Row.ROW_4, Column.B));
-
- board.executeMove(move);
-
- assertThat(board.getPieceAt(move.start())).isEmpty();
- assertThat(board.getPieceAt(move.end()))
- .isEqualTo(Optional.of(new Piece(Player.PLAYER_WHITE, false)));
- }
-
- @Test
- void remove_piece_between_jump_move() {
- Board board = new Board();
- Move move1 =
- Move.of(
- Player.PLAYER_WHITE,
- new BoardCoordinates(Row.ROW_3, Column.A),
- new BoardCoordinates(Row.ROW_4, Column.B));
- Move move2 =
- Move.of(
- Player.PLAYER_RED,
- new BoardCoordinates(Row.ROW_6, Column.B),
- new BoardCoordinates(Row.ROW_5, Column.C));
- Move move3 =
- Move.of(
- Player.PLAYER_WHITE,
- new BoardCoordinates(Row.ROW_3, Column.C),
- new BoardCoordinates(Row.ROW_4, Column.D));
- Move jumpMove =
- Move.of(
- Player.PLAYER_RED,
- new BoardCoordinates(Row.ROW_5, Column.C),
- new BoardCoordinates(Row.ROW_3, Column.A));
-
- List.of(move1, move2, move3, jumpMove).forEach(board::executeMove);
-
- assertThat(board.getPieceAt(move1.start()))
- .isEqualTo(Optional.of(new Piece(Player.PLAYER_RED, false)));
- assertThat(board.getPieceAt(move1.end())).isEqualTo(empty());
- }
-
- @Test
- void convert_piece_to_king_if_at_end_for_white() {
- Board board = new Board();
- // this move is not valid, but the board doesn't know that
- Move move =
- Move.of(
- Player.PLAYER_WHITE,
- new BoardCoordinates(Row.ROW_3, Column.A),
- new BoardCoordinates(Row.ROW_8, Column.B));
-
- board.executeMove(move);
-
- assertThat(board.getPieceAt(move.start())).isEqualTo(empty());
- assertThat(board.getPieceAt(move.end()))
- .isEqualTo(Optional.of(new Piece(Player.PLAYER_WHITE, true)));
- }
-
- @Test
- void convert_piece_to_king_if_at_end_for_red() {
- Board board = new Board();
- // this move is not valid, but the board doesn't know that
- Move move =
- Move.of(
- Player.PLAYER_RED,
- new BoardCoordinates(Row.ROW_6, Column.B),
- new BoardCoordinates(Row.ROW_1, Column.B));
-
- board.executeMove(move);
-
- assertThat(board.getPieceAt(move.start())).isEqualTo(empty());
- assertThat(board.getPieceAt(move.end()))
- .isEqualTo(Optional.of(new Piece(Player.PLAYER_RED, true)));
- }
-
- @Test
- void piece_stays_king_if_it_was_king() {
- Board board = new Board();
- // this move is not valid, but the board doesn't know that
- Move move1 =
- Move.of(
- Player.PLAYER_RED,
- new BoardCoordinates(Row.ROW_6, Column.B),
- new BoardCoordinates(Row.ROW_1, Column.B));
- Move move2 =
- Move.of(
- Player.PLAYER_RED,
- new BoardCoordinates(Row.ROW_1, Column.B),
- new BoardCoordinates(Row.ROW_2, Column.C));
-
- List.of(move1, move2).forEach(board::executeMove);
-
- assertThat(board.getPieceAt(move1.start())).isEmpty();
- assertThat(board.getPieceAt(move1.end())).isEmpty();
- assertThat(board.getPieceAt(move2.end()))
- .isEqualTo(Optional.of(new Piece(Player.PLAYER_RED, true)));
- }
-
- @Test
- void remove_start_piece_when_JumpGambleResult_is_lost() {
- Board board = new Board();
- Move move1 =
- Move.of(
- Player.PLAYER_WHITE,
- new BoardCoordinates(Row.ROW_3, Column.A),
- new BoardCoordinates(Row.ROW_4, Column.B));
- Move move2 =
- Move.of(
- Player.PLAYER_RED,
- new BoardCoordinates(Row.ROW_6, Column.B),
- new BoardCoordinates(Row.ROW_5, Column.C));
- Move move3 =
- Move.of(
- Player.PLAYER_WHITE,
- new BoardCoordinates(Row.ROW_3, Column.C),
- new BoardCoordinates(Row.ROW_4, Column.D));
- Move jumpMove =
- Move.of(
- Player.PLAYER_RED,
- new BoardCoordinates(Row.ROW_5, Column.C),
- new BoardCoordinates(Row.ROW_3, Column.A))
- .withJumpGambleResult(JumpGambleResult.LOST);
-
- List.of(move1, move2, move3, jumpMove).forEach(board::executeMove);
-
- assertThat(board.getPieceAt(jumpMove.start())).isEmpty();
- assertThat(board.getPieceAt(jumpMove.getCoordinatesBetween().orElseThrow())).isNotEmpty();
- }
-
- @Test
- void execute_normal_jump_move_if_JumpGambleResult_is_won() {
- Board board = new Board();
- Move move1 =
- Move.of(
- Player.PLAYER_WHITE,
- new BoardCoordinates(Row.ROW_3, Column.A),
- new BoardCoordinates(Row.ROW_4, Column.B));
- Move move2 =
- Move.of(
- Player.PLAYER_RED,
- new BoardCoordinates(Row.ROW_6, Column.B),
- new BoardCoordinates(Row.ROW_5, Column.C));
- Move move3 =
- Move.of(
- Player.PLAYER_WHITE,
- new BoardCoordinates(Row.ROW_3, Column.C),
- new BoardCoordinates(Row.ROW_4, Column.D));
- Move jumpMove =
- Move.of(
- Player.PLAYER_RED,
- new BoardCoordinates(Row.ROW_5, Column.C),
- new BoardCoordinates(Row.ROW_3, Column.A))
- .withJumpGambleResult(JumpGambleResult.WON);
-
- List.of(move1, move2, move3, jumpMove).forEach(board::executeMove);
-
- assertThat(board.getPieceAt(move1.start()))
- .isEqualTo(Optional.of(new Piece(Player.PLAYER_RED, false)));
- assertThat(board.getPieceAt(move1.end())).isEmpty();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/dom/MoveTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/dom/MoveTest.java
deleted file mode 100644
index eabf88d8..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/dom/MoveTest.java
+++ /dev/null
@@ -1,168 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.JumpGambleResult.*;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-import java.util.List;
-import java.util.Optional;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.ValueSource;
-
-class MoveTest {
- @Test
- void throw_for_empty_string() {
- assertThrows(IllegalArgumentException.class, () -> Move.parse(Player.PLAYER_WHITE, ""));
- }
-
- @Test
- void throw_if_string_contains_only_delimiter() {
- assertThrows(IllegalArgumentException.class, () -> Move.parse(Player.PLAYER_WHITE, "x"));
- }
-
- @Test
- void throw_if_string_contains_only_move_start() {
- assertThrows(IllegalArgumentException.class, () -> Move.parse(Player.PLAYER_WHITE, "a3x"));
- }
-
- @ParameterizedTest
- @ValueSource(
- strings = {
- "a9xb3", "i9xb3", "y9xb3", "a9x33", "a9xä3",
- })
- void throw_if_string_contains_invalid_column(String input) {
- assertThrows(IllegalArgumentException.class, () -> Move.parse(Player.PLAYER_WHITE, input));
- }
-
- @ParameterizedTest
- @ValueSource(
- strings = {
- "a9xb3", "a0xb3", "a-1xb3", "a0.5xb3", "a xb3",
- })
- void throw_if_string_contains_invalid_row(String input) {
- assertThrows(IllegalArgumentException.class, () -> Move.parse(Player.PLAYER_WHITE, input));
- }
-
- @Test
- void parse_correct_move() {
- Move expectedMove =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_3, A), new BoardCoordinates(ROW_4, B));
- assertThat(Move.parse(Player.PLAYER_WHITE, "a3xb4")).isEqualTo(expectedMove);
- assertThat(Move.parse(Player.PLAYER_WHITE, "A3xB4")).isEqualTo(expectedMove);
- assertThat(Move.parse(Player.PLAYER_WHITE, "a3Xb4")).isEqualTo(expectedMove);
- assertThat(Move.parse(Player.PLAYER_WHITE, "[a3]Xb4")).isEqualTo(expectedMove);
- assertThat(Move.parse(Player.PLAYER_WHITE, "[a3Xb4")).isEqualTo(expectedMove);
- assertThat(Move.parse(Player.PLAYER_WHITE, "[a3]X[b4]")).isEqualTo(expectedMove);
- }
-
- @Test
- void generate_possible_moves() {
- assertThat(
- Move.generatePossibleMoves(
- new BoardCoordinates(ROW_4, D), Player.PLAYER_WHITE, List.of(1, 2)))
- .hasSize(8);
- assertThat(
- Move.generatePossibleMoves(
- new BoardCoordinates(ROW_1, A), Player.PLAYER_WHITE, List.of(1, 2)))
- .hasSize(2);
- assertThat(
- Move.generatePossibleMoves(
- new BoardCoordinates(ROW_8, A), Player.PLAYER_WHITE, List.of(1, 2)))
- .hasSize(2);
- assertThat(
- Move.generatePossibleMoves(
- new BoardCoordinates(ROW_8, H), Player.PLAYER_WHITE, List.of(1, 2)))
- .hasSize(2);
- assertThat(
- Move.generatePossibleMoves(
- new BoardCoordinates(ROW_1, H), Player.PLAYER_WHITE, List.of(1, 2)))
- .hasSize(2);
- assertThat(
- Move.generatePossibleMoves(
- new BoardCoordinates(ROW_1, G), Player.PLAYER_WHITE, List.of(1, 2)))
- .hasSize(3);
- assertThat(
- Move.generatePossibleMoves(
- new BoardCoordinates(ROW_1, F), Player.PLAYER_WHITE, List.of(1, 2)))
- .hasSize(4);
- }
-
- @Test
- void return_empty_for_getCoordinatesBetween_for_simple_move() {
- Move simpleMove =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
- assertThat(simpleMove.getCoordinatesBetween()).isEmpty();
- }
-
- @Test
- void calculate_correct_coordinates_between_for_jump_move() {
- Move jumpMove =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_3, C));
- assertThat(jumpMove.getCoordinatesBetween())
- .isEqualTo(Optional.of(new BoardCoordinates(ROW_2, B)));
- }
-
- @Test
- void return_true_for_isJumpMove_on_jump_move() {
- Move jumpMove =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_3, C));
- assertThat(jumpMove.isJumpMove()).isTrue();
- }
-
- @Test
- void return_false_for_isJumpMove_on_move_from_same_position_to_same_position() {
- Move jumpMove =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_1, A));
- assertThat(jumpMove.isJumpMove()).isFalse();
- }
-
- @Test
- void return_false_for_isJumpMove_on_simple_move() {
- Move simpleMove =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
- assertThat(simpleMove.isJumpMove()).isFalse();
- }
-
- @Test
- void throw_if_withJumpGambleResult_called_with_WON_on_simple_move() {
- Move simpleMove =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
- assertThrows(IllegalArgumentException.class, () -> simpleMove.withJumpGambleResult(WON));
- }
-
- @Test
- void throw_if_withJumpGambleResult_called_with_LOST_on_simple_move() {
- Move simpleMove =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
- assertThrows(IllegalArgumentException.class, () -> simpleMove.withJumpGambleResult(LOST));
- }
-
- @Test
- void not_change_anything_if_withJumpGambleResult_called_with_NO_GAMBLE_on_simple_move() {
- Move simpleMove =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
- assertThat(simpleMove.withJumpGambleResult(NO_GAMBLE)).isEqualTo(simpleMove);
- }
-
- @Test
- void change_to_jumpGambleResult_given_with_withJumpGambleResult() {
- Move jumpMove =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_3, C));
- assertThat(jumpMove.withJumpGambleResult(WON).jumpGambleResult()).isEqualTo(WON);
- assertThat(jumpMove.withJumpGambleResult(NO_GAMBLE).jumpGambleResult()).isEqualTo(NO_GAMBLE);
- assertThat(jumpMove.withJumpGambleResult(LOST).jumpGambleResult()).isEqualTo(LOST);
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/StoreTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/StoreTest.java
deleted file mode 100644
index 37ab7f83..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/dom/board/StoreTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.dom.board;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row.*;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import java.util.Optional;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class StoreTest {
- private static final Piece WHITE_PIECE = new Piece(Player.PLAYER_WHITE, false);
- private static final BoardCoordinates EMPTY_COORDINATES_AT_START = new BoardCoordinates(ROW_4, A);
- private static final BoardCoordinates OCCUPIED_COORDINATES_AT_START =
- new BoardCoordinates(ROW_6, B);
- private static final Piece RED_PIECE = new Piece(Player.PLAYER_RED, false);
- private Store store;
-
- @BeforeEach
- void setup() {
- store = new Store();
- }
-
- @Test
- void add_piece_on_empty_place() {
- assertThat(store.getPieceAt(EMPTY_COORDINATES_AT_START)).isEmpty();
- store.addPiece(EMPTY_COORDINATES_AT_START, WHITE_PIECE);
- assertThat(store.getPieceAt(EMPTY_COORDINATES_AT_START)).isEqualTo(Optional.of(WHITE_PIECE));
- }
-
- @Test
- void add_piece_on_occupied_place() {
- assertThat(store.getPieceAt(OCCUPIED_COORDINATES_AT_START)).isEqualTo(Optional.of(RED_PIECE));
- store.addPiece(OCCUPIED_COORDINATES_AT_START, WHITE_PIECE);
- assertThat(store.getPieceAt(OCCUPIED_COORDINATES_AT_START)).isEqualTo(Optional.of(WHITE_PIECE));
- }
-
- @Test
- void remove_piece_at_empty_place() {
- store.removePiece(EMPTY_COORDINATES_AT_START);
- assertThat(store.getPieceAt(EMPTY_COORDINATES_AT_START)).isEmpty();
- }
-
- @Test
- void remove_piece_at_occupied_place() {
- store.removePiece(OCCUPIED_COORDINATES_AT_START);
- assertThat(store.getPieceAt(OCCUPIED_COORDINATES_AT_START)).isEmpty();
- }
-
- @Test
- void get_piece_of_empty_place() {
- assertThat(store.getPieceAt(EMPTY_COORDINATES_AT_START)).isEmpty();
- }
-
- @Test
- void get_piece_at_occupied_place() {
- assertThat(store.getPieceAt(OCCUPIED_COORDINATES_AT_START)).isEqualTo(Optional.of(RED_PIECE));
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveIsDiagonalTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveIsDiagonalTest.java
deleted file mode 100644
index 3a2cc5cc..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveIsDiagonalTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.JumpGambleResult.*;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class MoveIsDiagonalTest {
-
- private MoveIsDiagonal moveIsDiagonal;
- private Board board;
-
- @BeforeEach
- void setup() {
- moveIsDiagonal = new MoveIsDiagonal();
- board = new Board();
- }
-
- @Test
- void return_true_for_diagonal_move_up_right() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
-
- assertThat(moveIsDiagonal.validate(move, board)).isTrue();
- }
-
- @Test
- void return_true_for_diagonal_move_up_left() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, B), new BoardCoordinates(ROW_2, A));
-
- assertThat(moveIsDiagonal.validate(move, board)).isTrue();
- }
-
- @Test
- void return_true_for_diagonal_move_down_right() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_2, A), new BoardCoordinates(ROW_1, B));
-
- assertThat(moveIsDiagonal.validate(move, board)).isTrue();
- }
-
- @Test
- void return_true_for_diagonal_move_down_left() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_2, B), new BoardCoordinates(ROW_1, A));
-
- assertThat(moveIsDiagonal.validate(move, board)).isTrue();
- }
-
- @Test
- void return_true_for_diagonal_jump_move_up_right() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_3, C));
-
- assertThat(moveIsDiagonal.validate(move, board)).isTrue();
- }
-
- @Test
- void return_true_for_diagonal_jump_move_down_left() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_3, C), new BoardCoordinates(ROW_1, A));
-
- assertThat(moveIsDiagonal.validate(move, board)).isTrue();
- }
-
- @Test
- void return_false_for_move_to_the_right() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_1, B));
-
- assertThat(moveIsDiagonal.validate(move, board)).isFalse();
- }
-
- @Test
- void return_false_for_move_up() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_1, A));
-
- assertThat(moveIsDiagonal.validate(move, board)).isFalse();
- }
-
- @Test
- void return_false_for_move_one_up_and_two_right() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, C));
-
- assertThat(moveIsDiagonal.validate(move, board)).isFalse();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveLengthTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveLengthTest.java
deleted file mode 100644
index 2b8cfe49..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/MoveLengthTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.JumpGambleResult.*;
-import static org.assertj.core.api.Assertions.assertThat;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class MoveLengthTest {
- private MoveLength moveLength;
- private Board board;
-
- @BeforeEach
- void setup() {
- moveLength = new MoveLength();
- board = new Board();
- }
-
- @Test
- void return_true_if_player_moves_1_row() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, A));
- assertThat(moveLength.validate(move, board)).isTrue();
- }
-
- @Test
- void return_true_if_player_moves_2_row() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_3, A));
- assertThat(moveLength.validate(move, board)).isTrue();
- }
-
- @Test
- void return_false_if_player_moves_3_row() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_4, A));
- assertThat(moveLength.validate(move, board)).isFalse();
- }
-
- @Test
- void return_true_if_player_moves_7_row() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_8, A));
- assertThat(moveLength.validate(move, board)).isFalse();
- }
-
- @Test
- void return_true_if_player_moves_1_col() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_1, B));
- assertThat(moveLength.validate(move, board)).isTrue();
- }
-
- @Test
- void return_true_if_player_moves_2_col() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_1, C));
- assertThat(moveLength.validate(move, board)).isTrue();
- }
-
- @Test
- void return_false_if_player_moves_3_col() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_1, D));
- assertThat(moveLength.validate(move, board)).isFalse();
- }
-
- @Test
- void return_true_if_player_moves_7_col() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_1, H));
- assertThat(moveLength.validate(move, board)).isFalse();
- }
-
- @Test
- void return_true_if_player_moves_1_row_back() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_2, A), new BoardCoordinates(ROW_1, A));
- assertThat(moveLength.validate(move, board)).isTrue();
- }
-
- @Test
- void return_true_if_player_moves_1_col_back() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, B), new BoardCoordinates(ROW_1, A));
- assertThat(moveLength.validate(move, board)).isTrue();
- }
-
- @Test
- void return_true_if_player_moves_1_col_and_2_rows() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, B), new BoardCoordinates(ROW_3, A));
- assertThat(moveLength.validate(move, board)).isTrue();
- }
-
- @Test
- void return_false_if_move_does_not_change_position() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_1, A));
- assertThat(moveLength.validate(move, board)).isFalse();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/NoOtherMoveToJumpPossibleTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/NoOtherMoveToJumpPossibleTest.java
deleted file mode 100644
index c599197d..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/NoOtherMoveToJumpPossibleTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.JumpGambleResult.*;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.Optional;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class NoOtherMoveToJumpPossibleTest {
- private static final Piece WHITE_PAWN = new Piece(Player.PLAYER_WHITE, false);
- private static final Piece RED_PAWN = new Piece(Player.PLAYER_RED, false);
- private static final Piece RED_KING = new Piece(Player.PLAYER_RED, true);
-
- private NoOtherMoveToJumpPossible noOtherMoveToJumpPossible;
- private Board board;
-
- @BeforeEach
- void setup() {
- noOtherMoveToJumpPossible =
- new NoOtherMoveToJumpPossible(
- new MoveIsForwardIfNotKing(), new OpponentPieceBetweenJump(), new TargetFieldEmpty());
- board = mock(Board.class);
- }
-
- @Test
- void return_true_if_no_other_pieces_exist() {
- when(board.getPieceAt(notNull())).thenReturn(Optional.empty());
-
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
- assertThat(noOtherMoveToJumpPossible.validate(move, board)).isTrue();
- }
-
- @Test
- void return_true_if_move_is_jump_move() {
- when(board.getPieceAt(notNull())).thenReturn(Optional.empty());
-
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_3, C));
- assertThat(noOtherMoveToJumpPossible.validate(move, board)).isTrue();
- }
-
- @Test
- void does_not_crash_if_piece_is_at_edge() {
- when(board.getPieceAt(new BoardCoordinates(ROW_1, A))).thenReturn(Optional.of(WHITE_PAWN));
- when(board.getPieceAt(new BoardCoordinates(ROW_8, A))).thenReturn(Optional.of(WHITE_PAWN));
- when(board.getPieceAt(new BoardCoordinates(ROW_1, G))).thenReturn(Optional.of(WHITE_PAWN));
- when(board.getPieceAt(new BoardCoordinates(ROW_8, G))).thenReturn(Optional.of(WHITE_PAWN));
-
- Move move =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
- assertThat(noOtherMoveToJumpPossible.validate(move, board)).isTrue();
- }
-
- @Test
- void return_false_if_other_jump_move_possible_for_white() {
- when(board.getPieceAt(new BoardCoordinates(ROW_4, E))).thenReturn(Optional.of(WHITE_PAWN));
- when(board.getPieceAt(new BoardCoordinates(ROW_5, F))).thenReturn(Optional.of(RED_PAWN));
- when(board.getPieceAt(new BoardCoordinates(ROW_6, G))).thenReturn(Optional.empty());
-
- Move move =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
- assertThat(noOtherMoveToJumpPossible.validate(move, board)).isFalse();
- }
-
- @Test
- void return_false_if_other_jump_move_possible_for_red() {
- when(board.getPieceAt(new BoardCoordinates(ROW_6, G))).thenReturn(Optional.of(RED_PAWN));
- when(board.getPieceAt(new BoardCoordinates(ROW_5, F))).thenReturn(Optional.of(WHITE_PAWN));
- when(board.getPieceAt(new BoardCoordinates(ROW_4, E))).thenReturn(Optional.empty());
-
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
- assertThat(noOtherMoveToJumpPossible.validate(move, board)).isFalse();
- }
-
- @Test
- void return_false_if_other_jump_move_with_king_possible_for_red() {
- when(board.getPieceAt(new BoardCoordinates(ROW_4, E))).thenReturn(Optional.of(RED_KING));
- when(board.getPieceAt(new BoardCoordinates(ROW_5, F))).thenReturn(Optional.of(WHITE_PAWN));
- when(board.getPieceAt(new BoardCoordinates(ROW_6, G))).thenReturn(Optional.empty());
-
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
- assertThat(noOtherMoveToJumpPossible.validate(move, board)).isFalse();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/OpponentPieceBetweenJumpTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/OpponentPieceBetweenJumpTest.java
deleted file mode 100644
index f6511577..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/OpponentPieceBetweenJumpTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.JumpGambleResult.*;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.Optional;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class OpponentPieceBetweenJumpTest {
- private static final Piece WHITE_PAWN = new Piece(Player.PLAYER_WHITE, false);
- private static final Piece RED_PAWN = new Piece(Player.PLAYER_RED, false);
-
- private OpponentPieceBetweenJump opponentPieceBetweenJump;
- private Board board;
-
- @BeforeEach
- void setup() {
- opponentPieceBetweenJump = new OpponentPieceBetweenJump();
- board = mock(Board.class);
- }
-
- @Test
- void return_true_if_no_jump() {
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
- assertThat(opponentPieceBetweenJump.validate(move, board)).isTrue();
- }
-
- @Test
- void return_false_if_no_piece_between_jump() {
- when(board.getPieceAt(new BoardCoordinates(ROW_2, B))).thenReturn(Optional.empty());
-
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_3, C));
- assertThat(opponentPieceBetweenJump.validate(move, board)).isFalse();
- }
-
- @Test
- void return_true_if_piece_between_belongs_to_opponent_white() {
- when(board.getPieceAt(new BoardCoordinates(ROW_2, B))).thenReturn(Optional.of(RED_PAWN));
-
- Move move =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_3, A), new BoardCoordinates(ROW_1, C));
- assertThat(opponentPieceBetweenJump.validate(move, board)).isTrue();
- }
-
- @Test
- void return_true_if_piece_between_belongs_to_opponent_red() {
- when(board.getPieceAt(new BoardCoordinates(ROW_2, B))).thenReturn(Optional.of(WHITE_PAWN));
-
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, C), new BoardCoordinates(ROW_3, A));
- assertThat(opponentPieceBetweenJump.validate(move, board)).isTrue();
- }
-
- @Test
- void return_false_if_piece_between_belongs_to_same_player_white() {
- when(board.getPieceAt(new BoardCoordinates(ROW_2, B))).thenReturn(Optional.of(WHITE_PAWN));
-
- Move move =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_3, C), new BoardCoordinates(ROW_1, A));
- assertThat(opponentPieceBetweenJump.validate(move, board)).isFalse();
- }
-
- @Test
- void return_false_if_piece_between_belongs_to_same_player_red() {
- when(board.getPieceAt(notNull())).thenReturn(Optional.of(RED_PAWN));
-
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_3, C));
- assertThat(opponentPieceBetweenJump.validate(move, board)).isFalse();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/StartPieceValidTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/StartPieceValidTest.java
deleted file mode 100644
index a3dea968..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/StartPieceValidTest.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.JumpGambleResult.*;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.Optional;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class StartPieceValidTest {
- static final Piece WHITE_PAWN = new Piece(Player.PLAYER_WHITE, false);
- static final Piece RED_PAWN = new Piece(Player.PLAYER_RED, false);
-
- private StartPieceValid startPieceValid;
- private Board board;
-
- @BeforeEach
- void setup() {
- startPieceValid = new StartPieceValid();
- board = mock(Board.class);
- }
-
- @Test
- void return_false_if_no_piece() {
- when(board.getPieceAt(notNull())).thenReturn(Optional.empty());
-
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
-
- assertThat(startPieceValid.validate(move, board)).isFalse();
- }
-
- @Test
- void return_true_if_piece_belongs_to_player_red() {
- when(board.getPieceAt(notNull())).thenReturn(Optional.of(RED_PAWN));
-
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
-
- assertThat(startPieceValid.validate(move, board)).isTrue();
- }
-
- @Test
- void return_true_if_piece_belongs_to_player_white() {
- when(board.getPieceAt(notNull())).thenReturn(Optional.of(WHITE_PAWN));
-
- Move move =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
-
- assertThat(startPieceValid.validate(move, board)).isTrue();
- }
-
- @Test
- void return_false_if_piece_belongs_to_opponent_player_white() {
- when(board.getPieceAt(notNull())).thenReturn(Optional.of(RED_PAWN));
-
- Move move =
- Move.of(
- Player.PLAYER_WHITE, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
-
- assertThat(startPieceValid.validate(move, board)).isFalse();
- }
-
- @Test
- void return_false_if_piece_belongs_to_opponent_player_red() {
- when(board.getPieceAt(notNull())).thenReturn(Optional.of(WHITE_PAWN));
-
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, B));
-
- assertThat(startPieceValid.validate(move, board)).isFalse();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/TargetFieldEmptyTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/TargetFieldEmptyTest.java
deleted file mode 100644
index e290b25c..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/movevalidator/TargetFieldEmptyTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.movevalidator;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row.*;
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.JumpGambleResult.*;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.Optional;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class TargetFieldEmptyTest {
- public static final Piece PIECE = new Piece(Player.PLAYER_WHITE, false);
-
- private TargetFieldEmpty targetFieldEmpty;
- private Board board;
-
- @BeforeEach
- void setup() {
- targetFieldEmpty = new TargetFieldEmpty();
- board = mock(Board.class);
- }
-
- @Test
- void return_true_if_target_field_empty() {
- when(board.getPieceAt(notNull())).thenReturn(Optional.empty());
-
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, A));
- assertThat(targetFieldEmpty.validate(move, board)).isTrue();
- }
-
- @Test
- void return_false_if_target_field_not_empty() {
- when(board.getPieceAt(notNull())).thenReturn(Optional.of(PIECE));
-
- Move move =
- Move.of(Player.PLAYER_RED, new BoardCoordinates(ROW_1, A), new BoardCoordinates(ROW_2, A));
- assertThat(targetFieldEmpty.validate(move, board)).isFalse();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/util/BoardPrinterTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/util/BoardPrinterTest.java
deleted file mode 100644
index 55db415d..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/util/BoardPrinterTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.util;
-
-import static org.mockito.Mockito.*;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.Optional;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.mockito.InOrder;
-
-class BoardPrinterTest {
-
- private Board board;
- private Console console;
- private BoardPrinter boardPrinter;
- private InOrder inOrder;
-
- @BeforeEach
- void setup() {
- board = spy(new Board());
- console = mock(Console.class);
- boardPrinter = new BoardPrinter(console);
- inOrder = inOrder(console);
- }
-
- @Test
- void prints_initial_board_state() {
- boardPrinter.printBoard(board);
-
- inOrder.verify(console).print(" a b c d e f g h ");
- inOrder.verify(console).print(" +-------------------------------------------------+");
- inOrder.verify(console).print("8 | [ ] [R_P] [ ] [R_P] [ ] [R_P] [ ] [R_P] | 8");
- inOrder.verify(console).print("7 | [R_P] [ ] [R_P] [ ] [R_P] [ ] [R_P] [ ] | 7");
- inOrder.verify(console).print("6 | [ ] [R_P] [ ] [R_P] [ ] [R_P] [ ] [R_P] | 6");
- inOrder.verify(console).print("5 | [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] | 5");
- inOrder.verify(console).print("4 | [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] | 4");
- inOrder.verify(console).print("3 | [W_P] [ ] [W_P] [ ] [W_P] [ ] [W_P] [ ] | 3");
- inOrder.verify(console).print("2 | [ ] [W_P] [ ] [W_P] [ ] [W_P] [ ] [W_P] | 2");
- inOrder.verify(console).print("1 | [W_P] [ ] [W_P] [ ] [W_P] [ ] [W_P] [ ] | 1");
- inOrder.verify(console).print(" +-------------------------------------------------+");
- inOrder.verify(console).print(" a b c d e f g h ");
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- void print_king() {
- when(board.getPieceAt(new BoardCoordinates(Row.ROW_1, Column.A)))
- .thenReturn(Optional.of(new Piece(Player.PLAYER_WHITE, true)));
-
- boardPrinter.printBoard(board);
-
- inOrder.verify(console).print(" a b c d e f g h ");
- inOrder.verify(console).print(" +-------------------------------------------------+");
- inOrder.verify(console).print("8 | [ ] [R_P] [ ] [R_P] [ ] [R_P] [ ] [R_P] | 8");
- inOrder.verify(console).print("7 | [R_P] [ ] [R_P] [ ] [R_P] [ ] [R_P] [ ] | 7");
- inOrder.verify(console).print("6 | [ ] [R_P] [ ] [R_P] [ ] [R_P] [ ] [R_P] | 6");
- inOrder.verify(console).print("5 | [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] | 5");
- inOrder.verify(console).print("4 | [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] | 4");
- inOrder.verify(console).print("3 | [W_P] [ ] [W_P] [ ] [W_P] [ ] [W_P] [ ] | 3");
- inOrder.verify(console).print("2 | [ ] [W_P] [ ] [W_P] [ ] [W_P] [ ] [W_P] | 2");
- inOrder.verify(console).print("1 | [W_K] [ ] [W_P] [ ] [W_P] [ ] [W_P] [ ] | 1");
- inOrder.verify(console).print(" +-------------------------------------------------+");
- inOrder.verify(console).print(" a b c d e f g h ");
- inOrder.verifyNoMoreInteractions();
- }
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/util/CoinTosserTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/util/CoinTosserTest.java
deleted file mode 100644
index d054c26a..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/util/CoinTosserTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.util;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.Player.PLAYER_RED;
-import static ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser.Result.HEADS;
-import static ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser.Result.TAILS;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assumptions.assumeTrue;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.Map;
-import java.util.Random;
-import java.util.stream.IntStream;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class CoinTosserTest {
-
- private PointsCalculator pointsCalculator;
- private Random random;
- private Board board;
-
- @BeforeEach
- void setup() {
- pointsCalculator = mock(PointsCalculator.class);
- random = mock(RandomWrapper.class);
- board = mock(Board.class);
- }
-
- @Test
- void return_heads_and_tails_evenly_distributed() {
- when(pointsCalculator.calculatePoints(notNull()))
- .thenReturn(Map.of(PLAYER_RED, 1, Player.PLAYER_WHITE, 1));
- CoinTosser coinTosser = new CoinTosser(pointsCalculator);
- long count =
- IntStream.range(0, 100)
- .mapToObj(__ -> coinTosser.toss(board, PLAYER_RED))
- .filter(HEADS::equals)
- .count();
- // use assume because of the random element
- assumeTrue(count < 70);
- assumeTrue(count > 30);
- }
-
- @Test
- void use_even_chances_if_points_are_evenly_distributed() {
- when(pointsCalculator.calculatePoints(notNull()))
- .thenReturn(Map.of(PLAYER_RED, 1, Player.PLAYER_WHITE, 1));
- CoinTosser coinTosser = new CoinTosser(pointsCalculator, random);
-
- when(random.nextFloat()).thenReturn(0.5f);
- assertThat(coinTosser.toss(board, PLAYER_RED)).isEqualTo(HEADS);
-
- when(random.nextFloat()).thenReturn(0.51f);
- assertThat(coinTosser.toss(board, PLAYER_RED)).isEqualTo(TAILS);
- }
-
- @Test
- void increase_the_chance_for_the_weaker_player() {
- when(pointsCalculator.calculatePoints(notNull()))
- .thenReturn(Map.of(PLAYER_RED, 1, Player.PLAYER_WHITE, 2));
- CoinTosser coinTosser = new CoinTosser(pointsCalculator, random);
-
- when(random.nextFloat()).thenReturn(0.6666f);
- assertThat(coinTosser.toss(board, PLAYER_RED)).isEqualTo(HEADS);
-
- when(random.nextFloat()).thenReturn(0.66667f);
- assertThat(coinTosser.toss(board, PLAYER_RED)).isEqualTo(TAILS);
- }
-
- @Test
- void decrease_the_chance_for_the_stronger_player() {
- when(pointsCalculator.calculatePoints(notNull()))
- .thenReturn(Map.of(PLAYER_RED, 2, Player.PLAYER_WHITE, 1));
- CoinTosser coinTosser = new CoinTosser(pointsCalculator, random);
-
- when(random.nextFloat()).thenReturn(0.3333f);
- assertThat(coinTosser.toss(board, PLAYER_RED)).isEqualTo(HEADS);
-
- when(random.nextFloat()).thenReturn(0.33334f);
- assertThat(coinTosser.toss(board, PLAYER_RED)).isEqualTo(TAILS);
- }
-
- private static class RandomWrapper extends Random {}
-}
diff --git a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/util/PointsCalculatorTest.java b/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/util/PointsCalculatorTest.java
deleted file mode 100644
index a6cbc08a..00000000
--- a/topics/sw_concepts/code/pattern-examples/src/test/java/ch/scs/jumpstart/pattern/examples/checkers/util/PointsCalculatorTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package ch.scs.jumpstart.pattern.examples.checkers.util;
-
-import static ch.scs.jumpstart.pattern.examples.checkers.dom.Player.*;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Mockito.*;
-
-import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
-import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
-import java.util.Map;
-import java.util.Optional;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class PointsCalculatorTest {
- private PointsCalculator pointsCalculator;
- private Board board;
-
- @BeforeEach
- void setup() {
- pointsCalculator = new PointsCalculator();
- board = spy(new Board());
- }
-
- @Test
- void calculate_points_correctly_at_start() {
- assertThat(pointsCalculator.calculatePoints(board))
- .isEqualTo(Map.of(PLAYER_WHITE, 12, PLAYER_RED, 12));
- }
-
- @Test
- void calculate_points_if_no_pieces_are_on_board() {
- doReturn(Optional.empty()).when(board).getPieceAt(notNull());
- assertThat(pointsCalculator.calculatePoints(board))
- .isEqualTo(Map.of(PLAYER_WHITE, 0, PLAYER_RED, 0));
- }
-
- @Test
- void use_2_as_value_for_kings() {
- doReturn(Optional.of(new Piece(PLAYER_WHITE, true)), Optional.of(new Piece(PLAYER_RED, true)))
- .when(board)
- .getPieceAt(notNull());
- assertThat(pointsCalculator.calculatePoints(board))
- .isEqualTo(Map.of(PLAYER_WHITE, 2, PLAYER_RED, 126));
- }
-}
diff --git a/topics/sw_concepts/code/slides/abstract-factory/abstract-factory-bad-case.py b/topics/sw_concepts/code/slides/abstract-factory/abstract-factory-bad-case.py
new file mode 100644
index 00000000..144085f2
--- /dev/null
+++ b/topics/sw_concepts/code/slides/abstract-factory/abstract-factory-bad-case.py
@@ -0,0 +1,78 @@
+from abc import ABC, abstractmethod
+
+
+class Label(ABC):
+ @abstractmethod
+ def render(self) -> str:
+ pass
+
+
+class Button(ABC):
+ @abstractmethod
+ def render(self) -> str:
+ pass
+
+
+class LightLabel(Label):
+ def render(self) -> str:
+ return "Light Label"
+
+
+class DarkLabel(Label):
+ def render(self) -> str:
+ return "Dark Label"
+
+
+class LightButton(Button):
+ def render(self) -> str:
+ return "Light Button"
+
+
+class DarkButton(Button):
+ def render(self) -> str:
+ return "Dark Button"
+
+
+class Header:
+ def __init__(self, darkmode: bool) -> None:
+ if darkmode:
+ self.label: Label = DarkLabel()
+ else:
+ self.label = LightLabel()
+
+ def display(self) -> str:
+ return f"Header: {self.label.render()}"
+
+
+class MainView:
+ def __init__(self, darkmode: bool) -> None:
+ if darkmode:
+ self.label: Label = DarkLabel()
+ self.button: Button = DarkButton()
+ else:
+ self.label = LightLabel()
+ self.button = LightButton()
+
+ def display(self) -> str:
+ return f"MainView: {self.label.render()}, {self.button.render()}"
+
+
+class Bootstrap:
+ def __init__(self, darkmode: bool) -> None:
+ self.darkmode = darkmode
+ self.header = Header(darkmode)
+ self.main_view = MainView(darkmode)
+
+ def run(self) -> None:
+ print(self.header.display())
+ print(self.main_view.display())
+
+
+if __name__ == "__main__":
+ print("Light mode:")
+ light_app = Bootstrap(darkmode=False)
+ light_app.run()
+
+ print("\nDark mode:")
+ dark_app = Bootstrap(darkmode=True)
+ dark_app.run()
diff --git a/topics/sw_concepts/code/slides/abstract-factory/abstract-factory-good-case.py b/topics/sw_concepts/code/slides/abstract-factory/abstract-factory-good-case.py
new file mode 100644
index 00000000..bd2888c0
--- /dev/null
+++ b/topics/sw_concepts/code/slides/abstract-factory/abstract-factory-good-case.py
@@ -0,0 +1,101 @@
+from abc import ABC, abstractmethod
+
+
+class Label(ABC):
+ @abstractmethod
+ def render(self) -> str:
+ pass
+
+
+class Button(ABC):
+ @abstractmethod
+ def render(self) -> str:
+ pass
+
+
+class LightLabel(Label):
+ def render(self) -> str:
+ return "Light Label"
+
+
+class DarkLabel(Label):
+ def render(self) -> str:
+ return "Dark Label"
+
+
+class LightButton(Button):
+ def render(self) -> str:
+ return "Light Button"
+
+
+class DarkButton(Button):
+ def render(self) -> str:
+ return "Dark Button"
+
+
+class ComponentFactory(ABC):
+ @abstractmethod
+ def create_label(self) -> Label:
+ pass
+
+ @abstractmethod
+ def create_button(self) -> Button:
+ pass
+
+
+class LightComponentFactory(ComponentFactory):
+ def create_label(self) -> Label:
+ return LightLabel()
+
+ def create_button(self) -> Button:
+ return LightButton()
+
+
+class DarkComponentFactory(ComponentFactory):
+ def create_label(self) -> Label:
+ return DarkLabel()
+
+ def create_button(self) -> Button:
+ return DarkButton()
+
+
+class Header:
+ def __init__(self, factory: ComponentFactory) -> None:
+ self.label: Label = factory.create_label()
+
+ def display(self) -> str:
+ return f"Header: {self.label.render()}"
+
+
+class MainView:
+ def __init__(self, factory: ComponentFactory) -> None:
+ self.label: Label = factory.create_label()
+ self.button: Button = factory.create_button()
+
+ def display(self) -> str:
+ return f"MainView: {self.label.render()}, {self.button.render()}"
+
+
+class Bootstrap:
+ def __init__(self, darkmode: bool) -> None:
+ if darkmode:
+ factory: ComponentFactory = DarkComponentFactory()
+ else:
+ factory = LightComponentFactory()
+
+ self.header = Header(factory)
+ self.main_view = MainView(factory)
+
+ def run(self) -> None:
+ print(self.header.display())
+ print(self.main_view.display())
+
+
+if __name__ == "__main__":
+ print("Light mode:")
+ light_app = Bootstrap(darkmode=False)
+ light_app.run()
+
+ print("\nDark mode:")
+ dark_app = Bootstrap(darkmode=True)
+ dark_app.run()
diff --git a/topics/sw_concepts/code/slides/command/command-bad-case.py b/topics/sw_concepts/code/slides/command/command-bad-case.py
new file mode 100644
index 00000000..46510357
--- /dev/null
+++ b/topics/sw_concepts/code/slides/command/command-bad-case.py
@@ -0,0 +1,72 @@
+from abc import ABC, abstractmethod
+
+
+class ThemeObservable:
+ def __init__(self) -> None:
+ self._is_dark_theme: bool = False
+
+ def set_is_dark_theme(self, value: bool) -> None:
+ self._is_dark_theme = value
+ theme_name = "Dark" if value else "Light"
+ print(f"Theme changed to {theme_name}")
+
+ def is_dark_theme(self) -> bool:
+ return self._is_dark_theme
+
+
+class DarkThemeButton(ABC):
+ def __init__(self, theme: ThemeObservable) -> None:
+ self.theme = theme
+
+ @abstractmethod
+ def on_action(self) -> None:
+ pass
+
+
+class DarkDarkThemeButton(DarkThemeButton):
+ def on_action(self) -> None:
+ print("Dark theme button clicked (in dark UI)")
+ self.theme.set_is_dark_theme(True)
+
+
+class LightDarkThemeButton(DarkThemeButton):
+ def on_action(self) -> None:
+ print("Dark theme button clicked (in light UI)")
+ self.theme.set_is_dark_theme(True)
+
+
+class LightThemeButton(ABC):
+ def __init__(self, theme: ThemeObservable) -> None:
+ self.theme = theme
+
+ @abstractmethod
+ def on_action(self) -> None:
+ pass
+
+
+class DarkLightThemeButton(LightThemeButton):
+ def on_action(self) -> None:
+ print("Light theme button clicked (in dark UI)")
+ self.theme.set_is_dark_theme(False)
+
+
+class LightLightThemeButton(LightThemeButton):
+ def on_action(self) -> None:
+ print("Light theme button clicked (in light UI)")
+ self.theme.set_is_dark_theme(False)
+
+
+if __name__ == "__main__":
+ theme = ThemeObservable()
+
+ print("Starting in light mode:")
+ dark_btn = LightDarkThemeButton(theme) # Dark button for light UI
+ light_btn = LightLightThemeButton(theme) # Light button for light UI
+
+ dark_btn.on_action()
+
+ print("\nNow in dark mode, need different button instances:")
+ dark_btn_2 = DarkDarkThemeButton(theme) # Dark button for dark UI
+ light_btn_2 = DarkLightThemeButton(theme) # Light button for dark UI
+
+ light_btn_2.on_action()
diff --git a/topics/sw_concepts/code/slides/command/command-good-case.py b/topics/sw_concepts/code/slides/command/command-good-case.py
new file mode 100644
index 00000000..37b94704
--- /dev/null
+++ b/topics/sw_concepts/code/slides/command/command-good-case.py
@@ -0,0 +1,87 @@
+from abc import ABC, abstractmethod
+
+
+class ThemeObservable:
+ def __init__(self) -> None:
+ self._is_dark_theme: bool = False
+
+ def set_is_dark_theme(self, value: bool) -> None:
+ self._is_dark_theme = value
+ theme_name = "Dark" if value else "Light"
+ print(f"[Theme] Theme changed to {theme_name}")
+
+ def is_dark_theme(self) -> bool:
+ return self._is_dark_theme
+
+
+class Command(ABC):
+ @abstractmethod
+ def execute(self) -> None:
+ pass
+
+
+class SetDarkThemeCommand(Command):
+ def __init__(self, theme: ThemeObservable) -> None:
+ self.theme = theme
+
+ def execute(self) -> None:
+ print("Executing: Set Dark Theme")
+ self.theme.set_is_dark_theme(True)
+
+
+class SetLightThemeCommand(Command):
+ def __init__(self, theme: ThemeObservable) -> None:
+ self.theme = theme
+
+ def execute(self) -> None:
+ print("Executing: Set Light Theme")
+ self.theme.set_is_dark_theme(False)
+
+
+class DarkButton:
+ def __init__(self) -> None:
+ self._command: Command | None = None
+
+ def set_command(self, command: Command) -> None:
+ self._command = command
+
+ def on_action(self) -> None:
+ if self._command:
+ print("Dark button clicked")
+ self._command.execute()
+ else:
+ print("Dark button has no command set")
+
+
+class LightButton:
+ def __init__(self) -> None:
+ self._command: Command | None = None
+
+ def set_command(self, command: Command) -> None:
+ self._command = command
+
+ def on_action(self) -> None:
+ if self._command:
+ print("Light button clicked")
+ self._command.execute()
+ else:
+ print("Light button has no command set")
+
+
+if __name__ == "__main__":
+ theme = ThemeObservable()
+
+ dark_button = DarkButton()
+ dark_button.set_command(SetLightThemeCommand(theme))
+
+ light_button = LightButton()
+ light_button.set_command(SetDarkThemeCommand(theme))
+
+ print("Starting in light mode:")
+ dark_button.on_action()
+
+ print("\nNow in dark mode:")
+ light_button.on_action()
+
+ print("\nBack to dark mode:")
+ dark_button.on_action()
diff --git a/topics/sw_concepts/code/slides/dependency-inversion/no-dependency-inversion.py b/topics/sw_concepts/code/slides/dependency-inversion/no-dependency-inversion.py
new file mode 100644
index 00000000..a9f21277
--- /dev/null
+++ b/topics/sw_concepts/code/slides/dependency-inversion/no-dependency-inversion.py
@@ -0,0 +1,51 @@
+from datetime import datetime
+
+
+class Transaction:
+ def __init__(self, transaction_id: str, amount: float, timestamp: datetime) -> None:
+ self.transaction_id = transaction_id
+ self.amount = amount
+ self.timestamp = timestamp
+
+ def __repr__(self) -> str:
+ return f"Transaction({self.transaction_id}, ${self.amount:.2f}, {self.timestamp})"
+
+
+class DBTransactionRepository:
+ def __init__(self) -> None:
+ self._transactions: dict[str, Transaction] = {}
+
+ def save(self, transaction: Transaction) -> None:
+ print(f"[DB] Saving transaction to database: {transaction.transaction_id}")
+ self._transactions[transaction.transaction_id] = transaction
+
+ def find_by_id(self, transaction_id: str) -> Transaction | None:
+ print(f"[DB] Querying database for transaction: {transaction_id}")
+ return self._transactions.get(transaction_id)
+
+
+class TransactionService:
+ def __init__(self) -> None:
+ self.repository = DBTransactionRepository()
+
+ def create_transaction(self, transaction_id: str, amount: float) -> Transaction:
+ """Create and save a new transaction"""
+ transaction = Transaction(transaction_id, amount, datetime.now())
+ self.repository.save(transaction)
+ return transaction
+
+ def get_transaction(self, transaction_id: str) -> Transaction | None:
+ """Retrieve a transaction by ID"""
+ return self.repository.find_by_id(transaction_id)
+
+
+if __name__ == "__main__":
+ service = TransactionService()
+
+ print("Creating transaction:")
+ txn = service.create_transaction("TXN-001", 99.99)
+ print(f"Created: {txn}")
+
+ print("\nRetrieving transaction:")
+ retrieved = service.get_transaction("TXN-001")
+ print(f"Retrieved: {retrieved}")
diff --git a/topics/sw_concepts/code/slides/dependency-inversion/with-dependency-inversion.py b/topics/sw_concepts/code/slides/dependency-inversion/with-dependency-inversion.py
new file mode 100644
index 00000000..8f45e572
--- /dev/null
+++ b/topics/sw_concepts/code/slides/dependency-inversion/with-dependency-inversion.py
@@ -0,0 +1,80 @@
+from abc import ABC, abstractmethod
+from datetime import datetime
+
+
+class Transaction:
+ def __init__(self, transaction_id: str, amount: float, timestamp: datetime) -> None:
+ self.transaction_id = transaction_id
+ self.amount = amount
+ self.timestamp = timestamp
+
+ def __repr__(self) -> str:
+ return f"Transaction({self.transaction_id}, ${self.amount:.2f}, {self.timestamp})"
+
+
+class TransactionRepository(ABC):
+ @abstractmethod
+ def save(self, transaction: Transaction) -> None:
+ pass
+
+ @abstractmethod
+ def find_by_id(self, transaction_id: str) -> Transaction | None:
+ pass
+
+
+class TransactionService:
+ def __init__(self, repository: TransactionRepository) -> None:
+ self.repository = repository
+
+ def create_transaction(self, transaction_id: str, amount: float) -> Transaction:
+ transaction = Transaction(transaction_id, amount, datetime.now())
+ self.repository.save(transaction)
+ return transaction
+
+ def get_transaction(self, transaction_id: str) -> Transaction | None:
+ return self.repository.find_by_id(transaction_id)
+
+
+class DBTransactionRepository(TransactionRepository):
+ def __init__(self) -> None:
+ self._transactions: dict[str, Transaction] = {}
+
+ def save(self, transaction: Transaction) -> None:
+ print(f"[DB] Saving transaction to database: {transaction.transaction_id}")
+ self._transactions[transaction.transaction_id] = transaction
+
+ def find_by_id(self, transaction_id: str) -> Transaction | None:
+ print(f"[DB] Querying database for transaction: {transaction_id}")
+ return self._transactions.get(transaction_id)
+
+
+class InMemoryTransactionRepository(TransactionRepository):
+ def __init__(self) -> None:
+ self._transactions: dict[str, Transaction] = {}
+
+ def save(self, transaction: Transaction) -> None:
+ print(f"[Memory] Saving transaction to memory: {transaction.transaction_id}")
+ self._transactions[transaction.transaction_id] = transaction
+
+ def find_by_id(self, transaction_id: str) -> Transaction | None:
+ print(f"[Memory] Retrieving transaction from memory: {transaction_id}")
+ return self._transactions.get(transaction_id)
+
+
+if __name__ == "__main__":
+ print("Using DB Repository:")
+ db_repo = DBTransactionRepository()
+ service = TransactionService(db_repo)
+
+ txn = service.create_transaction("TXN-001", 99.99)
+ print(f"Created: {txn}")
+
+ retrieved = service.get_transaction("TXN-001")
+ print(f"Retrieved: {retrieved}")
+
+ print("\nUsing In-Memory Repository:")
+ memory_repo = InMemoryTransactionRepository()
+ service2 = TransactionService(memory_repo)
+
+ txn2 = service2.create_transaction("TXN-002", 49.99)
+ print(f"Created: {txn2}")
diff --git a/topics/sw_concepts/code/slides/factory/factory-bad-case.py b/topics/sw_concepts/code/slides/factory/factory-bad-case.py
new file mode 100644
index 00000000..3fef7be5
--- /dev/null
+++ b/topics/sw_concepts/code/slides/factory/factory-bad-case.py
@@ -0,0 +1,53 @@
+from abc import ABC, abstractmethod
+
+
+class Label(ABC):
+ @abstractmethod
+ def render(self) -> str:
+ pass
+
+
+class LightLabel(Label):
+ def render(self) -> str:
+ return "[Light Label]"
+
+
+class DarkLabel(Label):
+ def render(self) -> str:
+ return "[Dark Label]"
+
+
+class Header:
+ def __init__(self, is_dark_theme: bool) -> None:
+ if is_dark_theme:
+ self.title_label: Label = DarkLabel()
+ else:
+ self.title_label = LightLabel()
+
+ def display(self) -> str:
+ return f"Header: {self.title_label.render()}"
+
+
+class MainView:
+ def __init__(self, is_dark_theme: bool) -> None:
+ if is_dark_theme:
+ self.content_label: Label = DarkLabel()
+ else:
+ self.content_label = LightLabel()
+
+ def display(self) -> str:
+ return f"MainView: {self.content_label.render()}"
+
+
+if __name__ == "__main__":
+ print("Light theme:")
+ header_light = Header(is_dark_theme=False)
+ main_light = MainView(is_dark_theme=False)
+ print(header_light.display())
+ print(main_light.display())
+
+ print("\nDark theme:")
+ header_dark = Header(is_dark_theme=True)
+ main_dark = MainView(is_dark_theme=True)
+ print(header_dark.display())
+ print(main_dark.display())
diff --git a/topics/sw_concepts/code/slides/factory/factory-good-case.py b/topics/sw_concepts/code/slides/factory/factory-good-case.py
new file mode 100644
index 00000000..c93ed089
--- /dev/null
+++ b/topics/sw_concepts/code/slides/factory/factory-good-case.py
@@ -0,0 +1,59 @@
+from abc import ABC, abstractmethod
+
+
+class Label(ABC):
+ @abstractmethod
+ def render(self) -> str:
+ pass
+
+
+class LightLabel(Label):
+ def render(self) -> str:
+ return "[Light Label]"
+
+
+class DarkLabel(Label):
+ def render(self) -> str:
+ return "[Dark Label]"
+
+
+class LabelFactory:
+ def __init__(self, is_dark_theme: bool) -> None:
+ self.is_dark_theme = is_dark_theme
+
+ def create_label(self) -> Label:
+ if self.is_dark_theme:
+ return DarkLabel()
+ return LightLabel()
+
+
+class Header:
+ def __init__(self, factory: LabelFactory) -> None:
+ self.title_label: Label = factory.create_label()
+
+ def display(self) -> str:
+ return f"Header: {self.title_label.render()}"
+
+
+class MainView:
+ def __init__(self, factory: LabelFactory) -> None:
+ self.content_label: Label = factory.create_label()
+
+ def display(self) -> str:
+ return f"MainView: {self.content_label.render()}"
+
+
+if __name__ == "__main__":
+ print("Light theme:")
+ light_factory = LabelFactory(is_dark_theme=False)
+ header_light = Header(light_factory)
+ main_light = MainView(light_factory)
+ print(header_light.display())
+ print(main_light.display())
+
+ print("\nDark theme:")
+ dark_factory = LabelFactory(is_dark_theme=True)
+ header_dark = Header(dark_factory)
+ main_dark = MainView(dark_factory)
+ print(header_dark.display())
+ print(main_dark.display())
diff --git a/topics/sw_concepts/code/slides/interface-segregation/interface-segregation-bad-case.py b/topics/sw_concepts/code/slides/interface-segregation/interface-segregation-bad-case.py
new file mode 100644
index 00000000..d5c6fae0
--- /dev/null
+++ b/topics/sw_concepts/code/slides/interface-segregation/interface-segregation-bad-case.py
@@ -0,0 +1,46 @@
+from abc import ABC, abstractmethod
+
+
+class Washer(ABC):
+ @abstractmethod
+ def wash_clothes(self) -> None:
+ pass
+
+ @abstractmethod
+ def wash_dishes(self) -> None:
+ pass
+
+
+class HandWasher(Washer):
+ def wash_clothes(self) -> None:
+ print("Hand washing clothes in basin")
+
+ def wash_dishes(self) -> None:
+ print("Hand washing dishes in sink")
+
+
+class DishWashingMachine(Washer):
+ def wash_dishes(self) -> None:
+ print("Machine washing dishes with hot water and detergent")
+
+ def wash_clothes(self) -> None:
+ raise NotImplementedError(
+ "DishWashingMachine cannot wash clothes! This method should not exist for this class."
+ )
+
+
+if __name__ == "__main__":
+ print("HandWasher (flexible, can do both):")
+ hand_washer = HandWasher()
+ hand_washer.wash_clothes()
+ hand_washer.wash_dishes()
+
+ print("\nDishWashingMachine (specialized):")
+ dish_machine = DishWashingMachine()
+ dish_machine.wash_dishes()
+
+ print("\nTrying to wash clothes with dish machine:")
+ try:
+ dish_machine.wash_clothes()
+ except NotImplementedError as e:
+ print(f"Error: {e}")
diff --git a/topics/sw_concepts/code/slides/interface-segregation/interface-segregation-good-case.py b/topics/sw_concepts/code/slides/interface-segregation/interface-segregation-good-case.py
new file mode 100644
index 00000000..c487a3c5
--- /dev/null
+++ b/topics/sw_concepts/code/slides/interface-segregation/interface-segregation-good-case.py
@@ -0,0 +1,53 @@
+from abc import ABC, abstractmethod
+
+
+class ClothesWasher(ABC):
+ @abstractmethod
+ def wash_clothes(self) -> None:
+ pass
+
+
+class DishWasher(ABC):
+ @abstractmethod
+ def wash_dishes(self) -> None:
+ pass
+
+
+class HandWasher(ClothesWasher, DishWasher):
+ def wash_clothes(self) -> None:
+ print("Hand washing clothes in basin")
+
+ def wash_dishes(self) -> None:
+ print("Hand washing dishes in sink")
+
+
+class DishWashingMachine(DishWasher):
+ def wash_dishes(self) -> None:
+ print("Machine washing dishes with hot water and detergent")
+
+
+def clean_dishes(washer: DishWasher) -> None:
+ washer.wash_dishes()
+
+
+def clean_clothes(washer: ClothesWasher) -> None:
+ washer.wash_clothes()
+
+
+if __name__ == "__main__":
+ print("HandWasher (flexible, implements both interfaces):")
+ hand_washer = HandWasher()
+ hand_washer.wash_clothes()
+ hand_washer.wash_dishes()
+
+ print("\nDishWashingMachine (specialized, implements only DishWasher):")
+ dish_machine = DishWashingMachine()
+ dish_machine.wash_dishes()
+
+ print("\nUsing polymorphism with DishWasher interface:")
+ clean_dishes(hand_washer)
+ clean_dishes(dish_machine)
+
+ print("\nUsing ClothesWasher interface:")
+ clean_clothes(hand_washer)
+ # dish_machine cannot be passed here - it doesn't implement ClothesWasher!
diff --git a/topics/sw_concepts/code/slides/strategy/strategy-bad-case.py b/topics/sw_concepts/code/slides/strategy/strategy-bad-case.py
new file mode 100644
index 00000000..8ad8c07c
--- /dev/null
+++ b/topics/sw_concepts/code/slides/strategy/strategy-bad-case.py
@@ -0,0 +1,55 @@
+from abc import ABC, abstractmethod
+
+
+class Label(ABC):
+ @abstractmethod
+ def paint_background(self) -> None:
+ pass
+
+
+class LightLabel(Label):
+ def paint_background(self) -> None:
+ print(" Painting LIGHT background for label")
+ print(" - Setting background color: #FFFFFF")
+
+
+class DarkLabel(Label):
+ def paint_background(self) -> None:
+ print(" Painting DARK background for label")
+ print(" - Setting background color: #1E1E1E")
+
+
+class Button(ABC):
+ @abstractmethod
+ def paint_background(self) -> None:
+ pass
+
+
+class LightButton(Button):
+ def paint_background(self) -> None:
+ print(" Painting LIGHT background for button")
+ print(" - Setting background color: #FFFFFF")
+
+
+class DarkButton(Button):
+ def paint_background(self) -> None:
+ print(" Painting DARK background for button")
+ print(" - Setting background color: #1E1E1E")
+
+
+if __name__ == "__main__":
+ print("Light theme components:")
+ light_label = LightLabel()
+ light_button = LightButton()
+ print("Label:")
+ light_label.paint_background()
+ print("Button:")
+ light_button.paint_background()
+
+ print("\nDark theme components:")
+ dark_label = DarkLabel()
+ dark_button = DarkButton()
+ print("Label:")
+ dark_label.paint_background()
+ print("Button:")
+ dark_button.paint_background()
diff --git a/topics/sw_concepts/code/slides/strategy/strategy-good-case.py b/topics/sw_concepts/code/slides/strategy/strategy-good-case.py
new file mode 100644
index 00000000..a167ef59
--- /dev/null
+++ b/topics/sw_concepts/code/slides/strategy/strategy-good-case.py
@@ -0,0 +1,77 @@
+from abc import ABC, abstractmethod
+
+
+class PaintBackgroundStrategy(ABC):
+ @abstractmethod
+ def execute(self) -> None:
+ pass
+
+
+class LightBackgroundStrategy(PaintBackgroundStrategy):
+ def execute(self) -> None:
+ print(" Painting LIGHT background")
+ print(" - Setting background color: #FFFFFF")
+
+
+class DarkBackgroundStrategy(PaintBackgroundStrategy):
+ def execute(self) -> None:
+ print(" Painting DARK background")
+ print(" - Setting background color: #1E1E1E")
+
+
+class Label:
+ def __init__(self, background_strategy: PaintBackgroundStrategy) -> None:
+ self.background_strategy = background_strategy
+
+ def paint_background(self) -> None:
+ self.background_strategy.execute()
+
+
+class LightLabel(Label):
+ pass
+
+
+class DarkLabel(Label):
+ pass
+
+
+class Button:
+ def __init__(self, background_strategy: PaintBackgroundStrategy) -> None:
+ self.background_strategy = background_strategy
+
+ def paint_background(self) -> None:
+ self.background_strategy.execute()
+
+
+class LightButton(Button):
+ pass
+
+
+class DarkButton(Button):
+ pass
+
+
+if __name__ == "__main__":
+ light_strategy = LightBackgroundStrategy()
+ dark_strategy = DarkBackgroundStrategy()
+
+ print("Light theme components (using shared strategy):")
+ light_label = LightLabel(light_strategy)
+ light_button = LightButton(light_strategy)
+ print("Label:")
+ light_label.paint_background()
+ print("Button:")
+ light_button.paint_background()
+
+ print("\nDark theme components (using shared strategy):")
+ dark_label = DarkLabel(dark_strategy)
+ dark_button = DarkButton(dark_strategy)
+ print("Label:")
+ dark_label.paint_background()
+ print("Button:")
+ dark_button.paint_background()
+
+ print("\nChanging label strategy at runtime:")
+ light_label.background_strategy = dark_strategy
+ print("Light label with dark background:")
+ light_label.paint_background()
diff --git a/topics/sw_concepts/code/slides/uml/class-diagram-parts.py b/topics/sw_concepts/code/slides/uml/class-diagram-parts.py
new file mode 100644
index 00000000..fcf1df22
--- /dev/null
+++ b/topics/sw_concepts/code/slides/uml/class-diagram-parts.py
@@ -0,0 +1,37 @@
+from abc import ABC, abstractmethod
+
+
+class Bucket:
+ def __init__(self, capacity: int) -> None:
+ self.capacity = capacity
+
+ def __repr__(self) -> str:
+ return f"Bucket(capacity={self.capacity}L)"
+
+
+class Washer(ABC):
+ @abstractmethod
+ def wash_clothes(self) -> None:
+ pass
+
+ @abstractmethod
+ def wash_dishes(self) -> None:
+ pass
+
+
+class HandWasher(Washer):
+ def __init__(self, bucket: Bucket) -> None:
+ self.bucket = bucket
+
+ def wash_clothes(self) -> None:
+ print(f"Washing clothes using {self.bucket}")
+
+ def wash_dishes(self) -> None:
+ print(f"Washing dishes using {self.bucket}")
+
+
+if __name__ == "__main__":
+ bucket = Bucket(capacity=10)
+ hand_washer = HandWasher(bucket)
+ hand_washer.wash_clothes()
+ hand_washer.wash_dishes()
diff --git a/topics/sw_concepts/code/solution/01-intro-refactored.py b/topics/sw_concepts/code/solution/01-intro-refactored.py
new file mode 100644
index 00000000..471788f9
--- /dev/null
+++ b/topics/sw_concepts/code/solution/01-intro-refactored.py
@@ -0,0 +1,95 @@
+import math
+from dataclasses import dataclass
+
+TAX_RATE = 0.19
+DISCOUNT_THRESHOLD = 50.0
+DISCOUNT_RATE = 0.10
+
+EXCHANGE_RATES: dict[str, float] = {
+ "CHF": 1.0,
+ "USD": 1.08,
+}
+
+
+@dataclass
+class Product:
+ name: str
+ price: float
+
+
+@dataclass
+class LineItem:
+ product: Product
+ quantity: int
+
+ @property
+ def subtotal(self) -> float:
+ return self.quantity * self.product.price
+
+
+class Order:
+ def __init__(self, customer: str, items: list[LineItem], currency: str = "CHF") -> None:
+ self.customer = customer
+ self.items = items
+ self.currency = currency
+
+ @property
+ def subtotal(self) -> float:
+ return sum(item.subtotal for item in self.items)
+
+ @property
+ def discount(self) -> float:
+ if self.subtotal > DISCOUNT_THRESHOLD:
+ return self.subtotal * DISCOUNT_RATE
+ return 0.0
+
+ @property
+ def taxable_amount(self) -> float:
+ return self.subtotal - self.discount
+
+ @property
+ def tax(self) -> float:
+ return self.taxable_amount * TAX_RATE
+
+ @property
+ def total(self) -> float:
+ raw = self.taxable_amount + self.tax
+ return _floor_cents(raw * EXCHANGE_RATES[self.currency])
+
+
+def _floor_cents(value: float) -> float:
+ return math.floor(value * 100) / 100
+
+
+def print_receipt(order: Order) -> None:
+ print("=" * 40)
+ print("RECEIPT")
+ print("=" * 40)
+ for item in order.items:
+ print(f" {item.quantity}x {item.product.name} @ {item.product.price} = {item.subtotal}")
+ if order.discount > 0:
+ print(f" Discount ({DISCOUNT_RATE:.0%}): -{order.discount:.2f}")
+ print(f" Tax ({TAX_RATE:.0%}): {order.tax:.2f}")
+ print(f" TOTAL: {order.total} {order.currency}")
+ print("=" * 40)
+
+
+def main() -> None:
+ widget = Product("Widget", 9.99)
+ gadget = Product("Gadget", 24.99)
+
+ orders = [
+ Order("Alice", [LineItem(widget, 3), LineItem(gadget, 1)]),
+ Order("Bob", [LineItem(widget, 5)]),
+ Order("Charlie", [LineItem(widget, 4), LineItem(gadget, 2)], currency="USD"),
+ ]
+
+ for i, order in enumerate(orders, start=1):
+ print(f"Order {i} ({order.customer}): {order.total} {order.currency}")
+
+ print()
+ print_receipt(orders[0])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/topics/sw_concepts/code/solution/02-interfaces.py b/topics/sw_concepts/code/solution/02-interfaces.py
new file mode 100644
index 00000000..dd2d4109
--- /dev/null
+++ b/topics/sw_concepts/code/solution/02-interfaces.py
@@ -0,0 +1,56 @@
+from abc import ABC, abstractmethod
+from enum import StrEnum, auto
+
+
+class Washer(ABC):
+ @abstractmethod
+ def wash_clothes(self, item: str):
+ pass
+
+ @abstractmethod
+ def wash_dishes(self, item: str):
+ pass
+
+
+class MachineWasher:
+ def wash_clothes(self, item: str):
+ print(f"Machine washing {item}.")
+
+ def wash_dishes(self, item: str):
+ print(f"Machine washing {item}.")
+
+
+class HandWasher:
+ def wash_clothes(self, item: str):
+ print(f"Hand washing {item}.")
+
+ def wash_dishes(self, item: str):
+ print(f"Hand washing {item}.")
+
+
+class ThingsToWash(StrEnum):
+ PANTS = auto()
+ SHIRT = auto()
+ SPOON = auto()
+ PLATE = auto()
+ FORK = auto()
+
+
+def wash_things(washer: Washer, things: list[ThingsToWash]) -> None:
+ for thing in things:
+ if thing in (ThingsToWash.PANTS, ThingsToWash.SHIRT):
+ washer.wash_clothes(str(thing))
+ elif thing in (ThingsToWash.SPOON, ThingsToWash.PLATE, ThingsToWash.FORK):
+ washer.wash_dishes(str(thing))
+
+
+def main() -> None:
+ machine_washer = MachineWasher()
+ hand_washer = HandWasher()
+
+ wash_things(machine_washer, [ThingsToWash.PLATE, ThingsToWash.SHIRT, ThingsToWash.SPOON])
+ wash_things(hand_washer, [ThingsToWash.PANTS, ThingsToWash.FORK])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/topics/sw_concepts/code/solution/03-factory.py b/topics/sw_concepts/code/solution/03-factory.py
new file mode 100644
index 00000000..3e84b74b
--- /dev/null
+++ b/topics/sw_concepts/code/solution/03-factory.py
@@ -0,0 +1,69 @@
+from abc import ABC, abstractmethod
+from enum import StrEnum, auto
+
+
+class Washer(ABC):
+ @abstractmethod
+ def wash_clothes(self, item: str):
+ pass
+
+ @abstractmethod
+ def wash_dishes(self, item: str):
+ pass
+
+
+class MachineWasher:
+ def wash_clothes(self, item: str):
+ print(f"Machine washing {item}.")
+
+ def wash_dishes(self, item: str):
+ print(f"Machine washing {item}.")
+
+
+class HandWasher:
+ def wash_clothes(self, item: str):
+ print(f"Hand washing {item}.")
+
+ def wash_dishes(self, item: str):
+ print(f"Hand washing {item}.")
+
+
+class ThingsToWash(StrEnum):
+ PANTS = auto()
+ SHIRT = auto()
+ SPOON = auto()
+ PLATE = auto()
+ FORK = auto()
+
+
+def wash_things(washer: Washer, things: list[ThingsToWash]) -> None:
+ for thing in things:
+ if thing in (ThingsToWash.PANTS, ThingsToWash.SHIRT):
+ washer.wash_clothes(str(thing))
+ elif thing in (ThingsToWash.SPOON, ThingsToWash.PLATE, ThingsToWash.FORK):
+ washer.wash_dishes(str(thing))
+
+
+def washer_factory(can_wash_fragile_things: bool) -> Washer:
+ if can_wash_fragile_things:
+ return HandWasher()
+ return MachineWasher()
+
+
+def main() -> None:
+ washer = washer_factory(can_wash_fragile_things=True)
+
+ wash_things(
+ washer,
+ [
+ ThingsToWash.PLATE,
+ ThingsToWash.SHIRT,
+ ThingsToWash.SPOON,
+ ThingsToWash.PANTS,
+ ThingsToWash.FORK,
+ ],
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/topics/sw_concepts/code/solution/04-abstract-factory.py b/topics/sw_concepts/code/solution/04-abstract-factory.py
new file mode 100644
index 00000000..4d791d0e
--- /dev/null
+++ b/topics/sw_concepts/code/solution/04-abstract-factory.py
@@ -0,0 +1,104 @@
+from abc import ABC, abstractmethod
+from enum import StrEnum, auto
+
+
+class ClothesWasher(ABC):
+ @abstractmethod
+ def wash(self, item: str) -> None:
+ pass
+
+
+class DishWasher(ABC):
+ @abstractmethod
+ def wash(self, item: str) -> None:
+ pass
+
+
+class MachineClothesWasher(ClothesWasher):
+ def wash(self, item: str) -> None:
+ print(f"Machine washing clothes: {item}.")
+
+
+class MachineDishWasher(DishWasher):
+ def wash(self, item: str) -> None:
+ print(f"Machine washing dishes: {item}.")
+
+
+class HandClothesWasher(ClothesWasher):
+ def wash(self, item: str) -> None:
+ print(f"Hand washing clothes: {item}.")
+
+
+class HandDishWasher(DishWasher):
+ def wash(self, item: str) -> None:
+ print(f"Hand washing dishes: {item}.")
+
+
+class WasherFactory(ABC):
+ @abstractmethod
+ def create_clothes_washer(self) -> ClothesWasher:
+ pass
+
+ @abstractmethod
+ def create_dish_washer(self) -> DishWasher:
+ pass
+
+
+class MachineWasherFactory(WasherFactory):
+ def create_clothes_washer(self) -> ClothesWasher:
+ return MachineClothesWasher()
+
+ def create_dish_washer(self) -> DishWasher:
+ return MachineDishWasher()
+
+
+class HandWasherFactory(WasherFactory):
+ def create_clothes_washer(self) -> ClothesWasher:
+ return HandClothesWasher()
+
+ def create_dish_washer(self) -> DishWasher:
+ return HandDishWasher()
+
+
+class ThingsToWash(StrEnum):
+ PANTS = auto()
+ SHIRT = auto()
+ SPOON = auto()
+ PLATE = auto()
+ FORK = auto()
+
+
+def wash_things(factory: WasherFactory, things: list[ThingsToWash]) -> None:
+ clothes_washer = factory.create_clothes_washer()
+ dish_washer = factory.create_dish_washer()
+
+ for thing in things:
+ if thing in (ThingsToWash.PANTS, ThingsToWash.SHIRT):
+ clothes_washer.wash(str(thing))
+ elif thing in (ThingsToWash.SPOON, ThingsToWash.PLATE, ThingsToWash.FORK):
+ dish_washer.wash(str(thing))
+
+
+def get_washer_factory(can_wash_fragile_things: bool) -> WasherFactory:
+ if can_wash_fragile_things:
+ return HandWasherFactory()
+ return MachineWasherFactory()
+
+
+def main() -> None:
+ factory = get_washer_factory(can_wash_fragile_things=True)
+
+ wash_things(
+ factory,
+ [
+ ThingsToWash.PLATE,
+ ThingsToWash.SHIRT,
+ ThingsToWash.SPOON,
+ ThingsToWash.PANTS,
+ ThingsToWash.FORK,
+ ],
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/topics/sw_concepts/code/solution/05-observer-good-case.py b/topics/sw_concepts/code/solution/05-observer-good-case.py
new file mode 100644
index 00000000..83bb4369
--- /dev/null
+++ b/topics/sw_concepts/code/solution/05-observer-good-case.py
@@ -0,0 +1,94 @@
+from abc import ABC, abstractmethod
+
+
+class ThemeObserver(ABC):
+ @abstractmethod
+ def on_theme_changed(self, is_dark_theme: bool) -> None:
+ pass
+
+
+class ThemeObservable:
+ def __init__(self) -> None:
+ self._is_dark_theme: bool = False
+ self._observers: list[ThemeObserver] = []
+
+ def add_observer(self, observer: ThemeObserver) -> None:
+ if observer not in self._observers:
+ self._observers.append(observer)
+
+ def set_is_dark_theme(self, value: bool) -> None:
+ if self._is_dark_theme != value:
+ self._is_dark_theme = value
+ theme_name = "Dark" if value else "Light"
+ print(f"[Theme] Theme changed to {theme_name}")
+ self._notify_observers()
+
+ def _notify_observers(self) -> None:
+ for observer in self._observers:
+ observer.on_theme_changed(self._is_dark_theme)
+
+
+class DarkThemeButton(ThemeObserver):
+ def __init__(self, theme_observable: ThemeObservable) -> None:
+ self.theme_observable: ThemeObservable = theme_observable
+ self.enabled: bool = True
+
+ def on_action(self) -> None:
+ if not self.enabled:
+ return
+
+ print("Dark theme button clicked")
+ self.theme_observable.set_is_dark_theme(True)
+
+ def on_theme_changed(self, is_dark_theme: bool) -> None:
+ self.set_enable(not is_dark_theme)
+
+ def set_enable(self, enable: bool) -> None:
+ self.enabled = enable
+ status = "enabled" if enable else "disabled"
+ print(f" Dark theme button is now {status}")
+
+
+class LightThemeButton(ThemeObserver):
+ def __init__(self, theme_observable: ThemeObservable) -> None:
+ self.theme_observable: ThemeObservable = theme_observable
+ self.enabled: bool = False
+
+ def on_action(self) -> None:
+ if not self.enabled:
+ return
+
+ print("Light theme button clicked")
+ self.theme_observable.set_is_dark_theme(False)
+
+ def on_theme_changed(self, is_dark_theme: bool) -> None:
+ self.set_enable(is_dark_theme)
+
+ def set_enable(self, enable: bool) -> None:
+ self.enabled = enable
+ status = "enabled" if enable else "disabled"
+ print(f" Light theme button is now {status}")
+
+
+def main() -> None:
+ theme: ThemeObservable = ThemeObservable()
+
+ dark_button: DarkThemeButton = DarkThemeButton(theme)
+ light_button: LightThemeButton = LightThemeButton(theme)
+
+ theme.add_observer(dark_button)
+ theme.add_observer(light_button)
+
+ # Simulate user interactions
+ print("User clicks dark theme button:")
+ dark_button.on_action()
+
+ print("\nUser clicks light theme button:")
+ light_button.on_action()
+
+ print("\nUser clicks dark theme button again:")
+ dark_button.on_action()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/topics/sw_concepts/sw_concept_slides.md b/topics/sw_concepts/sw_concept_slides.md
index 20798fb3..c72ac055 100644
--- a/topics/sw_concepts/sw_concept_slides.md
+++ b/topics/sw_concepts/sw_concept_slides.md
@@ -5,16 +5,21 @@ title: "SW Konzepte"
date: \today
---
-Was ist hier falsch?
+Was ist hier schlecht?
-------
-Code Beispiel: [Abstractions.java](code/pattern-examples/src/main/java/ch/scs/jumpstart/pattern/examples/Abstractions.java)
+Code Beispiel: [01-intro.py](code/exercise/01-intro.py)
-Abstractions.java Zusammenfassung
+intro.py Zusammenfassung
-------
-* Fast keine Strukturierung durch Methoden/Funktionen
+* Fast keine Strukturierung durch Methoden/Funktionen/Datenmodelle
* Code duplication
+* Magic numbers
+* Business Logik und Präsentation vermischt
+
+-> Besser: [01-intro-refactored.py](code/solution/01-intro-refactored.py)
+
Abstraktion: Nachteile
-------
@@ -24,33 +29,6 @@ Abstraktion: Nachteile
* Die Abstraktion am falschen Ort (Vertikal)
* Die Abstraktion versteckt wichtige Features
-Abstraktion am falschen Ort
-------
-
-```java
-public void initialize() {
- carouselDisposable.dispose();
- carouselDisposable = contactlessPMod.getCarouselProvider()
- .addCarouselObserver(
- imagePath -> JfxThreadingUtils.invokeOnFx(() -> setContactlessLogo(imagePath))
- );
-}
-
-private void setContactlessLogo(String imagePath) {
- if (imagePath == null || getAcceptance() != ACCEPTED) {
- final String imageFileName = getImageFileName()
- final String imageUrl = IMAGES_PATH + imageFileName;
- contactlessLogo.setImage(new Image(imageUrl));
- } else {
- if (contactlessPMod.isCarouselEnabled()) {
- contactlessLogo.setImage(new Image(imagePath));
- } else {
- contactlessLogo.setImage(new Image(IMAGES_PATH + "static_carousel.png"));
- }
- }
-}
-```
-
Agenda
-------
@@ -96,21 +74,14 @@ KISS
(Keep it simple stupid)
```python
-# num % (modulo) 2 with a bitwise operator:
-num & 1
-# it is simpler and more readable
-# to use the % operator.
-num % 2
-```
-
-Erklärung Modulo 2 mit Bit operator
--------
-
-```md
-5 = 00000101 &
-1 = 00000001
---------------
- 00000001 -> ungerade
+# Good example (KISS)
+result = sum(numbers) / len(numbers)
+
+# Bad example (unnecessarily complex)
+result = 0
+for num in numbers:
+ result += num
+result = result / len(numbers)
```
YAGNI
@@ -136,7 +107,19 @@ NIH
NIH Beispiel 1
-------
-
+```python
+def custom_sort(numbers):
+ for i in range(len(numbers)):
+ for j in range(i + 1, len(numbers)):
+ if numbers[i] > numbers[j]:
+ numbers[i], numbers[j] = numbers[j], numbers[i]
+ return numbers
+
+sorted_list = custom_sort([3, 1, 4, 1, 5])
+
+# Besser: built-in Funktion verwenden
+sorted_list = sorted([3, 1, 4, 1, 5])
+```
Clean Code
-------
@@ -236,7 +219,24 @@ class HandWasher
Kurze Übung Interface
-------
-Überlegt euch zu zweit ein Beispiel für ein Interface mit 2 Methoden und 2 Implementationen.
+Implementiert das Interface `Washer` für die Klasse `MashineWasher` und `HandWasher` in Python.
+(Verwendet simple print statements für die Implementierung der Methoden.)
+
+Das Interface in Python könnte so aussehen (siehe auch [exercise/02-03-interfaces.py](code/exercise/02-03-interfaces.py)):
+
+```python
+from abc import ABC, abstractmethod
+
+class Washer(ABC):
+ @abstractmethod
+ def wash_clothes(self, item: str):
+ pass
+
+ @abstractmethod
+ def wash_dishes(self, item: str):
+ pass
+```
+
UML
-------
@@ -252,7 +252,14 @@ von [UML: ISO/IEC 19501](https://www.omg.org/spec/UML/ISO/19501/PDF)
UML Klassendiagramm
-------
-{width=70%}
+\colBegin{0.8}
+{width=90%}
+\colNext{0.2}
+\small
+
+[class-diagram-parts.py](code/slides/uml/class-diagram-parts.py)
+
+\colEnd
Single Responsibility
-------
@@ -299,7 +306,6 @@ public SessionFactoryBuilder withUserAndPassword(String username, String passwor
this.password = password;
return this;
}
-
```
Open Closed
@@ -317,8 +323,9 @@ Open Closed Beispiel: vorher
\colNext{0.2}
\small
+* [abstract-factory-bad-case.py](code/slides/abstract-factory/abstract-factory-bad-case.py)
* Das Modul `views` ist `closed for extension`.
-Für GreyLabel und GreyButton müssen alle if statements geändert werden.
+* Für GreyLabel und GreyButton müssen alle if statements geändert werden.
\colEnd
@@ -330,6 +337,7 @@ Open Closed Beispiel: nachher
\colNext{0.2}
\small
+* [abstract-factory-good-case.py](code/slides/abstract-factory/abstract-factory-good-case.py)
* Das Modul `views` ist jetzt `open` für weitere Implementationen von ComponentFactory.
\colEnd
@@ -347,6 +355,7 @@ Beispiel einer Verletzung des Liskov substitution principle

+
Das Liskov Substitution Principle ist hier verletzt, da die `DishWashingMachine::washClothes`
Methode sich nicht so verhält, wie das Interface verlangt.\
`DishWashingMachine` kann nicht überall dort eingesetzt werden, so das Interface `Washer` verlangt wird.
@@ -365,12 +374,16 @@ Interface Segregation Beispiel vorher

+[interface-segregation-bad-case.py](code/slides/interface-segregation/interface-segregation-bad-case.py)
+
Interface Segregation Beispiel nachher
------

+[interface-segregation-bad-case.py](code/slides/interface-segregation/interface-segregation-bad-case.py)
+
Hier lösen wir die Verletzung des Liskov Substitution Principles mit Interface Segregation.
Dependency Inversion
@@ -386,10 +399,14 @@ Dependency Inversion Beispiel 1
{width=65%}
+[no-dependency-inversion.py](code/slides/dependency-inversion/no-dependency-inversion.py)
+
Dependency Inversion Beispiel 2
-----
-{height=95%}
+{height=80%}
+
+[with-dependency-inversion.py](code/slides/dependency-inversion/with-dependency-inversion.py)
Design Patterns
-------
@@ -416,11 +433,27 @@ Factory Beispiel: vorher
{width=78%}
+[factory-bad-case.py](code/slides/factory/factory-bad-case.py)
+
Factory Beispiel: nachher
------

+[factory-good-case.py](code/slides/factory/factory-good-case.py)
+
+Factory Übung
+------
+
+Erweitert die Übung [exercise/02-03-interfaces.py](code/exercise/02-03-interfaces.py) um eine Factory,
+die `Washer`-Klasse erstellt basierend darauf, ob die Items empfindlich (fragile) sind oder nicht.
+Die Factory könnte so aussehen (muss nicht zwangsläufig eine klasse sein):
+
+```python
+def washer_factory(can_wash_fragile_things: bool) -> Washer:
+ pass
+```
+
AbstractFactory
-------
@@ -439,6 +472,9 @@ AbstractFactory Beispiel: vorher
\colNext{0.2}
\colEnd
+[abstract-factory-bad-case.py](code/slides/abstract-factory/abstract-factory-bad-case.py)
+
+
AbstractFactory Beispiel: nachher
------
@@ -447,6 +483,7 @@ AbstractFactory Beispiel: nachher
\colNext{0.2}
\small
+* [abstract-factory-good-case.py](code/slides/abstract-factory/abstract-factory-good-case.py)
* Die Anzahl "if darkmode" statements ist reduziert.
* Es ist jetzt viel einfacher, ein "HighContrast"-Theme einzubauen.
@@ -458,6 +495,24 @@ AbstractFactory Beispiel 2


+AbstractFactory Übung
+------
+
+Wir haben die vorherige Übung etwas erweitert (Interface Segregation).
+Nutzt [04-abstract-factory.py](code/exercise/04-abstract-factory.py) als Ausgangspunkt um eine Abstract Factory zu implementieren.
+
+```python
+class WasherFactory(ABC):
+ @abstractmethod
+ def create_clothes_washer(self) -> ClothesWasher:
+ pass
+
+ @abstractmethod
+ def create_dish_washer(self) -> DishWasher:
+ pass
+
+```
+
Strategy
-------
@@ -468,18 +523,21 @@ Strategy
* Beispiele:
* Unterschiedliche Algorithmen für ein Ziel (z.b. Suchalgorithmen, Sortieralgorithmen)
* Daten können von verschiedenen Quellen kommen (RemoteConfigSourceStrategy, FileConfigSourceStrategy)
-
Strategy Beispiel: vorher
-------
-
+
+
+[strategy-bad-case.py](code/slides/strategy/strategy-bad-case.py)
Strategy Beispiel: nachher
-------
-
+
+
+[strategy-good-case.py](code/slides/strategy/strategy-good-case.py)
Observer
-------
@@ -494,11 +552,27 @@ Observer Beispiel: vorher

+[05-observer-bad-case.py](code/exercises/05-observer-bad-case.py)
+
Observer Beispiel: nachher
-------
{height=90%}
+Observer Übung
+-------
+
+Implementiert das Observer Pattern für die folgende Situation.
+Nehmt dafür
+[05-observer-bad-case.py](code/exercises/05-observer-bad-case.py) als Ausgangspunkt.
+
+{height=75%}
+
+Observer Lösung
+-------
+
+Lösung: [05-observer-good-case.py](code/solutions/05-observer-good-case.py)
+
Command
-------
@@ -509,12 +583,16 @@ Command
Command Beispiel: vorher
-------
-
+
+
+[command-bad-case.py](code/slides/command/command-bad-case.py)
Command Beispiel: nachher
-------
-{height=90%}
+{height=85%}
+
+[command-good-case.py](code/slides/command/command-good-case.py)
Zusammenfassung Abstraktion
------
diff --git a/topics/tools_and_varia/slides/links.md b/topics/tools_and_varia/slides/links.md
index ed45dcfb..5a95eb0a 100644
--- a/topics/tools_and_varia/slides/links.md
+++ b/topics/tools_and_varia/slides/links.md
@@ -96,6 +96,7 @@ Links
[active_forks]: https://github.com/techgaun/active-forks
[vulnerabilities_in_code_examples]: https://arxiv.org/ftp/arxiv/papers/1910/1910.01321.pdf
[quality_chatgpt_as_code_help]: https://www.golem.de/news/ki-im-kreuzfeuer-der-kritik-massive-qualitaetsmaengel-bei-chatgpt-als-programmierhilfe-2405-185442.html
+[scs-copilot-infos]: https://supercomputingsystems.atlassian.net/wiki/x/6oMhIQ
* [wiki_signal_slot]
* [nano_signal_slot]
@@ -107,5 +108,6 @@ Links
* [active_forks]
* [vulnerabilities_in_code_examples]
* [quality_chatgpt_as_code_help]
+* [scs-copilot-infos]
\colEnd
diff --git a/topics/tools_and_varia/slides/problem_solving.md b/topics/tools_and_varia/slides/problem_solving.md
index a554e7f5..dfbbcbd0 100644
--- a/topics/tools_and_varia/slides/problem_solving.md
+++ b/topics/tools_and_varia/slides/problem_solving.md
@@ -105,6 +105,18 @@ Verwendung von Libraries aus dem Internet
* Gibt es bessere Forks? [active_forks]
+AI Coding Assistants
+--------------------
+
+Die meisten IDEs unterstützen mittlerweile AI Coding Assistants, die den Programmierprozess unterstützen können.
+In der SCS verwenden wir z.B. GitHub Copilot (siehe [scs-copilot-infos] für weitere Infos).
+
+* Copilot über die SCS beziehen: [scs-copilot-infos]
+* Immer in Absprache mit dem PL und Team, ob und wie AI-Tools eingesetzt werden dürfen.
+* Wie immer mit AI-Tools: *Kritisch hinterfragen, was vorgeschlagen wird und nicht blind übernehmen!*
+* Das Tool kann durchaus nützlich sein — man sollte etwas Erfahrung sammeln, wie es am besten eingesetzt werden kann.
+
+
Verwendung von Code aus dem Internet
------------------------------------