diff --git a/courses/backend/README.md b/courses/backend/README.md
index 0825b4ba..796e91cf 100644
--- a/courses/backend/README.md
+++ b/courses/backend/README.md
@@ -77,6 +77,7 @@ Total: 18 weeks
- [ ] Use logging and debugging tools to monitor and troubleshoot applications
- [ ] Connect to databases and implement CRUD operations
- [ ] Test APIs using Postman
+- [ ] Document APIs using Swagger/OpenAPI
### [Specialist Career Training](/shared-modules/specialist-career-training)
diff --git a/courses/frontend/advanced-javascript/week4/README.md b/courses/frontend/advanced-javascript/week4/README.md
index 75d2dd93..23fe4226 100644
--- a/courses/frontend/advanced-javascript/week4/README.md
+++ b/courses/frontend/advanced-javascript/week4/README.md
@@ -18,8 +18,16 @@ By the end of this session, you will be able to:
- [ ] Instantiate objects from classes using `new`
- [ ] Use Methods and constructors
- [ ] Use Static methods
- - [ ] Use inheritance with `extends` and `super()`
- [ ] Understand the difference between classes vs objects
+- [ ] Use **inheritance** and **composition** to share behavior between classes
+ - [ ] Use inheritance with `extends` and `super()`
+ - [ ] Recognize when inheritance is a good fit ("is-a" relationship)
+ - [ ] Use composition ("has-a") as an alternative to inheritance
+- [ ] _(optional)_ Recognise common **design patterns** and when to apply them
+ - [ ] Strategy — swap behavior by passing in a different object
+ - [ ] Factory — hide object creation complexity behind a function
+ - [ ] Observer — notify listeners when state changes
+ - [ ] Singleton — ensure only one instance of a class exists
```js
class Comment {
diff --git a/courses/frontend/advanced-javascript/week4/assignment.md b/courses/frontend/advanced-javascript/week4/assignment.md
index 1374fbd2..0baaa057 100644
--- a/courses/frontend/advanced-javascript/week4/assignment.md
+++ b/courses/frontend/advanced-javascript/week4/assignment.md
@@ -4,6 +4,8 @@ For this week's assignment we will create a web application that generates a scr
We use [Rapid API](https://rapidapi.com/apishub/api/website-screenshot6/?utm_source=RapidAPI.com%2Fguides&utm_medium=DevRel&utm_campaign=DevRel) to generate a screenshot and the [crudcrud API](https://crudcrud.com/) to save the screenshot.
+
+
## Technical specifications
1. User can enter a URL for a website and it will send back a screenshot of the website using the website-screenshot API
@@ -21,6 +23,89 @@ Look at your interface and think about what parts can be modeled as classes —
For the error system, think about what kinds of errors can happen in your app — what if the user submits an empty URL? What if the API returns a bad response? What if the network is down? You might end up with classes like `ValidationError`, `ApiError`, or something else entirely — it's up to you.
+---
+
+## API Guides
+
+### The Screenshot API (Rapid API)
+
+Sign up at [RapidAPI](https://rapidapi.com) and subscribe to the **website-screenshot6** API (free tier is enough). You will get an API key.
+
+| | |
+| ---------------- | -------------------------------------------------------------------------------------- |
+| **Method** | `GET` |
+| **URL** | `https://website-screenshot6.p.rapidapi.com/screenshot` |
+| **Query params** | `url` (URL-encoded), `width`, `height` |
+| **Headers** | `x-rapidapi-host: website-screenshot6.p.rapidapi.com` · `x-rapidapi-key: YOUR_API_KEY` |
+| **Response** | JSON — `{ screenshotUrl: "https://..." }` — use the value directly as an `` |
+
+> **Keep your API key out of git.** Put it in a `secret.js` file and add that file to `.gitignore`.
+
+---
+
+### The crudcrud API
+
+[crudcrud.com](https://crudcrud.com/) gives you a free, temporary REST API endpoint for storing JSON data. Go to the site and you will get a unique ID — your endpoint will look like:
+
+```text
+https://crudcrud.com/api/YOUR_UNIQUE_ID
+```
+
+You can create any resource name you like after it, for example `/screenshots`. For this app you need three operations:
+
+| What you want to do | Method | URL | Body / Notes |
+| ------------------------- | -------- | --------------------- | -------------------------------------- |
+| Get all saved screenshots | `GET` | `.../screenshots` | — |
+| Save a new screenshot | `POST` | `.../screenshots` | JSON with the fields you want to store |
+| Delete one screenshot | `DELETE` | `.../screenshots/:id` | — |
+
+crudcrud automatically assigns an `_id` field to each item you POST. You will need that `_id` to delete items later. POST requests must include `Content-Type: application/json` in the headers.
+
+> **Note:** crudcrud endpoints expire after a few days on the free plan. If your app suddenly stops working, go to crudcrud.com and get a new unique ID. Keep your unique ID in `secret.js` alongside your API key.
+
+---
+
+## Using `render()` — when and how
+
+The `render()` method is how a class puts itself on the page. The idea: **the class owns its own DOM element**. Call `render()` to create or update that element, then append the returned element somewhere in the DOM.
+
+Use this base class as a starting point — every UI class in your app should extend it:
+
+```js
+class UIComponent {
+ constructor() {
+ this.element = null;
+ }
+
+ render() {
+ throw new Error("render() must be implemented by subclass");
+ }
+}
+```
+
+A `Screenshot` class is a natural fit here — think about what data it needs, what its card looks like, and what it can do (hint: deleting itself is a good method).
+
+**When to call `render()`:**
+
+- Right after creating a new instance — to show it on screen
+- After data on the instance changes and the DOM should reflect it
+
+---
+
+## Error handling — when and how
+
+Not all errors are the same. A user typing nothing in the input is different from the API being down. Custom error classes let you handle each case differently.
+
+Think about what kinds of errors can happen in your app — validation failures, API problems, network issues. Each could be its own class that `extends Error`, with a method that returns a user-friendly message. Then use `try/catch` with `instanceof` to handle each type differently.
+
+**Where to use error handling in this app:**
+
+- When the user submits the form: validate that the URL field is not empty
+- When calling the screenshot API: catch network failures or non-2xx responses
+- When calling crudcrud (save, load, delete): catch failures and tell the user
+
+---
+
## Optional Tasks/Assignments
> **Note:** Users do not need to be stored in a database or API — just keep them in memory (e.g. an array of instances in your JavaScript). No need to persist them anywhere.
@@ -31,4 +116,6 @@ For the error system, think about what kinds of errors can happen in your app
4. Create another user. When saving a screenshot, also save the user email (or another unique identifier).
5. Make sure you only show screenshots that the logged-in user has uploaded.
-Keep in mind the API key for the website-screenshot and the uuid for crudcrud should be in a secret.js file which is not committed to git.
+---
+
+> Keep in mind the API key for the website-screenshot API and the unique ID for crudcrud should be in a `secret.js` file which is not committed to git.
diff --git a/courses/frontend/advanced-javascript/week4/session-materials/assignment-mockup.svg b/courses/frontend/advanced-javascript/week4/session-materials/assignment-mockup.svg
new file mode 100644
index 00000000..01aba576
--- /dev/null
+++ b/courses/frontend/advanced-javascript/week4/session-materials/assignment-mockup.svg
@@ -0,0 +1,82 @@
+
diff --git a/courses/frontend/advanced-javascript/week4/session-materials/code-inspiration.md b/courses/frontend/advanced-javascript/week4/session-materials/code-inspiration.md
index 4df83aab..e56f640b 100644
--- a/courses/frontend/advanced-javascript/week4/session-materials/code-inspiration.md
+++ b/courses/frontend/advanced-javascript/week4/session-materials/code-inspiration.md
@@ -148,6 +148,118 @@ Promise.all([fetch("/a"), fetch("/b")]);
Promise.race([fetch("/a"), fetch("/b")]);
```
+## Inheritance
+
+A child class inherits all properties and methods from a parent. `extends` = "is-a". `super()` calls the parent's constructor — must come before using `this`.
+
+```js
+class Vehicle {
+ constructor(brand, speed) {
+ this.brand = brand;
+ this.speed = speed;
+ }
+ move() {
+ console.log(`${this.brand} is moving`);
+ }
+}
+
+class Car extends Vehicle {
+ constructor(brand, speed, doors) {
+ super(brand, speed); // calls Vehicle's constructor
+ this.doors = doors;
+ }
+ honk() {
+ console.log("Beep!");
+ }
+}
+
+const car = new Car("Tesla", 0, 4);
+car.move(); // inherited from Vehicle
+car.honk(); // Car's own method
+```
+
+**When inheritance gets awkward** — the child is forced to break or override parent behavior:
+
+```js
+class Vehicle {
+ refuel() {
+ console.log("Filling up the tank...");
+ }
+}
+
+// ElectricCar IS A Vehicle, but refuel() makes no sense for it
+class ElectricCar extends Vehicle {
+ refuel() {
+ throw new Error("I don't use fuel!");
+ }
+}
+```
+
+When you find yourself overriding methods just to disable them, that's a sign to use composition instead.
+
+## Composition
+
+Instead of inheriting behavior, a class HAS parts. Each part is its own class. This is the "has-a" relationship.
+
+```js
+class Engine {
+ start() {
+ console.log("Engine started");
+ }
+}
+
+class GPS {
+ navigate(to) {
+ console.log(`Navigating to ${to}`);
+ }
+}
+
+class Car {
+ constructor(brand) {
+ this.brand = brand;
+ this.engine = new Engine();
+ this.gps = new GPS();
+ }
+ start() {
+ this.engine.start();
+ }
+ goTo(address) {
+ this.gps.navigate(address);
+ }
+}
+```
+
+**Passing dependencies in** — instead of creating the engine inside, receive it from outside. This lets you swap behaviors without changing the class:
+
+```js
+class ElectricEngine {
+ start(brand) {
+ console.log(`${brand}: electric engine humming`);
+ }
+}
+
+class GasEngine {
+ start(brand) {
+ console.log(`${brand}: gas engine roaring`);
+ }
+}
+
+class Car {
+ constructor(brand, engine) {
+ this.brand = brand;
+ this.engine = engine; // passed in from outside
+ }
+ start() {
+ this.engine.start(this.brand);
+ }
+}
+
+new Car("Tesla", new ElectricEngine()).start(); // "Tesla: electric engine humming"
+new Car("Ford", new GasEngine()).start(); // "Ford: gas engine roaring"
+```
+
+**Rule of thumb:** favor composition. Use inheritance only when there's a clear, stable "is-a" relationship.
+
## (Optional) Extending built-ins: Error and Web Components
`Error` is a built-in class; custom errors use `extends` and `super()` like any other subclass. Web Components apply the same “class + lifecycle + HTML” idea to the platform.
@@ -187,3 +299,115 @@ try {
// customElements.define("my-comment", CommentElement);
//
```
+
+## (Optional) Design Patterns
+
+Named solutions to problems that keep showing up. Use these only when they genuinely fit — don't force them.
+
+### Strategy Pattern
+
+Swap behavior by passing in a different object. This is the composition idea taken one step further.
+
+```js
+const electric = {
+ start(b) {
+ console.log(`${b}: humming`);
+ },
+};
+const gas = {
+ start(b) {
+ console.log(`${b}: roaring`);
+ },
+};
+const hybrid = {
+ start(b) {
+ console.log(`${b}: both!`);
+ },
+};
+
+// Same class, different strategy → different behavior
+new Car("Tesla", electric).start();
+new Car("Ford", gas).start();
+new Car("Toyota", hybrid).start();
+```
+
+**When to use it:** multiple ways to do the same thing (sorting, validation, auth); you want to switch behavior without modifying the class itself.
+
+### Factory Pattern
+
+A function that creates objects for you — hides `new` and setup logic from the caller.
+
+```js
+function createCar(type, brand) {
+ const engines = {
+ electric: {
+ start(b) {
+ console.log(`${b}: humming`);
+ },
+ },
+ gas: {
+ start(b) {
+ console.log(`${b}: roaring`);
+ },
+ },
+ };
+ return new Car(brand, engines[type]);
+}
+
+const tesla = createCar("electric", "Tesla");
+const ford = createCar("gas", "Ford");
+```
+
+**When to use it:** object creation is complex (many params, config, dependencies); you want to centralize and hide construction details.
+
+### Observer Pattern
+
+"When something happens, notify everyone who cares." This is how `addEventListener`, Node's `EventEmitter`, and most UI frameworks work under the hood.
+
+```js
+class Order {
+ constructor() {
+ this.listeners = [];
+ this.status = "pending";
+ }
+
+ onChange(fn) {
+ this.listeners.push(fn);
+ }
+
+ updateStatus(newStatus) {
+ this.status = newStatus;
+ this.listeners.forEach((fn) => fn(this.status));
+ }
+}
+
+const order = new Order();
+order.onChange((s) => console.log(`Customer notified: ${s}`));
+order.onChange((s) => console.log(`Driver notified: ${s}`));
+order.updateStatus("ready"); // both callbacks fire
+```
+
+### Singleton Pattern
+
+Only one instance ever exists. Every call to `new` returns the same object.
+
+```js
+class Database {
+ constructor(url) {
+ if (Database.instance) return Database.instance;
+ this.url = url;
+ this.connected = false;
+ Database.instance = this;
+ }
+ connect() {
+ this.connected = true;
+ }
+}
+
+const db1 = new Database("postgres://...");
+const db2 = new Database("mysql://...");
+console.log(db1 === db2); // true — same instance!
+```
+
+**Good for:** DB connections, config, logging, caches — things you truly need only one of.
+**Use sparingly:** singletons are global state in disguise. They make testing harder and hide dependencies.
diff --git a/courses/frontend/advanced-javascript/week4/session-materials/exercises.md b/courses/frontend/advanced-javascript/week4/session-materials/exercises.md
index a375086f..64c32d47 100644
--- a/courses/frontend/advanced-javascript/week4/session-materials/exercises.md
+++ b/courses/frontend/advanced-javascript/week4/session-materials/exercises.md
@@ -2,17 +2,19 @@
Work through these in order.
-## 1. Create a user class
+## 1. User class with DOM rendering
-The class should have 2 properties: `firstName` and `lastName`. Hint: Use `this` and `constructor`.
+### 1. Create a user class
-## 2. Create an instance of the class
+Create a `User` class with 2 properties: `firstName` and `lastName`. Hint: use `this` and `constructor`.
+
+### 2. Create an instance of the class
Use the `new` keyword and assign the instance in a variable.
Add a **`renderUserCard(user)`** function that accepts a **`User`** instance and renders a user card on the page (e.g. a `div` with `firstName` and `lastName`).
-## 3. Create a class method
+### 3. Create a class method
1. Add **`getFullName`**: it should return the combined first and last name of the user. Use string concatenation or template literals and **`this`** to read the properties.
@@ -20,7 +22,7 @@ Add a **`renderUserCard(user)`** function that accepts a **`User`** instance and
3. Call **`myUser.render()`** so the card appears on the page (you can stop using **`renderUserCard`** once this works).
-## 4. Creating a CV class
+## 2. Creating a CV class
The CV that we will be making uses three classes: `Job`, `Education` and
`CV`. The `CV` class we have made for you (with some missing functionality). The `Job` and `Education` classes you need to create.
@@ -87,3 +89,53 @@ class CV {
### Part 4
Add a method to the `CV` class called `renderCV()`. This method should render out the CV using HTML. Make sure, that view updates, when data is changed.
+
+## 3. Design Challenge: FoodDash
+
+You're building a food delivery app. Customers browse restaurants, add items to their order, and a driver picks it up and delivers it.
+
+**Rules:** paper only — no code yet!
+
+For each class you identify, write down:
+
+- Its name
+- Its properties
+- Its methods
+- How it relates to the other classes
+
+Think about:
+
+1. What classes do you need?
+2. What properties does each class have?
+3. What methods does each class need?
+4. How do the classes relate to each other?
+5. Does anything share behavior? How would you handle that?
+
+When done, compare your design with others: which classes did different people pick? Did anyone make `Driver extends User`? How did you handle the `Order`/`Restaurant` relationship?
+
+## Bonus: Build FoodDash
+
+Now that you've designed FoodDash on paper, build it in code.
+
+1. Create a `Restaurant` class with a `name` property and a `menu` property (array of items, each with a `name` and `price`).
+2. Create an `Order` class that takes an array of items and a `Restaurant` instance.
+ - Add an `addItem(item)` method and a `removeItem(item)` method.
+ - Add an `async calculateTotal()` method that sums the prices of all items in the order.
+3. Create a `User` class that receives a `name`, `email`, and `role` object via the constructor. The role object must have a `perform(name)` method.
+ - Add a `doWork()` method that calls `this.role.perform(this.name)`.
+4. Create two role objects (`customerRole` and `driverRole`), each with a `perform(name)` method that logs what that role does.
+
+**Bonus:** Add a `static Order.sortByTotal(orders)` method that sorts an array of orders by total price.
+
+## Challenge: Monster Arena
+
+A turn-based monster battle game — design and OOP in action.
+
+You have a starter project in [`./oop-monster-arena/`](./oop-monster-arena/). Follow the instructions in its README.
+
+The challenge covers:
+
+- Modeling game entities as classes (`Monster`, `Arena`, `Attack`)
+- Using composition to give monsters different attack strategies
+- Inheritance for shared monster behavior
+- Turn-based game loop logic
diff --git a/courses/frontend/advanced-javascript/week4/session-materials/js_oop_classes.pdf b/courses/frontend/advanced-javascript/week4/session-materials/js_oop_classes.pdf
new file mode 100644
index 00000000..0e566337
Binary files /dev/null and b/courses/frontend/advanced-javascript/week4/session-materials/js_oop_classes.pdf differ
diff --git a/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/.gitignore b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/.gitignore
new file mode 100644
index 00000000..62ad6bc9
--- /dev/null
+++ b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/.gitignore
@@ -0,0 +1,4 @@
+.claude
+node_modules
+dist
+docs
\ No newline at end of file
diff --git a/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/ABILITY_EXAMPLES.md b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/ABILITY_EXAMPLES.md
new file mode 100644
index 00000000..d29150ac
--- /dev/null
+++ b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/ABILITY_EXAMPLES.md
@@ -0,0 +1,158 @@
+# Ability Examples
+
+A reference of ability ideas you can build from.
+
+---
+
+## The two rules to remember
+
+> **Higher `amount` = lower trigger chance.** The budget is fixed, so if you return `50`, it fires ~30% of the time. If you return `10`, it fires ~100% (or 80% for HealAbility). Big swings are rare; small effects are reliable.
+
+> **Fewer charges = higher trigger chance.** Limited-charge abilities get a budget bonus to compensate. A 1-charge ability fires up to 5× more reliably than an unlimited one at the same amount — so a single-use nuke can be near-guaranteed.
+
+---
+
+## Budget & trigger chance
+
+Every ability type has a fixed **budget**. The trigger chance is calculated automatically:
+
+```text
+triggerChance = (budget × chargeMultiplier) / amount (capped at 100%)
+```
+
+### Base budgets (unlimited / ≥ 5 charges)
+
+| Type | Budget | amount 8 | amount 15 | amount 20 | amount 30 | amount 50 |
+| ------------- | ------ | -------- | --------- | --------- | --------- | --------- |
+| DamageAbility | 15 | 100% | 100% | 75% | 50% | 30% |
+| HealAbility | 12 | 100% | 80% | 60% | 40% | 24% |
+| ArmorAbility | 8 | 100% | 53% | 40% | 27% | 16% |
+
+### Charge multiplier
+
+Charges are part of the budget. An ability with limited charges can fire fewer times per bout, so each trigger is worth more — the budget scales up automatically.
+
+```text
+chargeMultiplier = 5 / min(charges, 5)
+```
+
+| charges | multiplier | effective DamageAbility budget |
+| ------- | ---------- | ------------------------------ |
+| ∞ or ≥5 | ×1.0 | 15 |
+| 3 | ×1.67 | 25 |
+| 2 | ×2.5 | 37.5 |
+| 1 | ×5.0 | 75 |
+
+**Examples with charges:**
+
+- `new DamageAbility(30)` → 50% trigger, fires every turn it can (unlimited)
+- `new DamageAbility(30, 3)` → **83%** trigger, fires at most 3 times
+- `new DamageAbility(30, 1)` → **100%** trigger, fires exactly once (guaranteed)
+- `new HealAbility(20)` → 60% trigger, unlimited
+- `new HealAbility(20, 2)` → **100%** trigger, fires at most twice
+- `new ArmorAbility(20)` → 40% trigger, unlimited
+- `new ArmorAbility(20, 1)` → **100%** trigger, fires exactly once (guaranteed)
+
+When `activate()` returns a **variable** amount, the charge multiplier still applies and the chance is recalculated each turn based on whatever value you return.
+
+---
+
+## DamageAbility
+
+Deals extra damage to the opponent each time it triggers.
+
+**Flat bonus** — simplest case, always hits for the same extra damage.
+
+```text
+activate() returns 20 — triggers 75% of the time.
+```
+
+**Rage** — tracks how many hits you've taken; the more damage received, the harder the next hit.
+
+```text
+activate() returns 10 + (hitsReceived × 5).
+Each hit you absorb adds 5 to the next ability trigger.
+describe() says "Warlord retaliates with X fury damage!"
+```
+
+**Finishing blow** — checks opponent's HP; explodes when they're low.
+
+```text
+activate() returns 50 if opponent.hp.current < 30, else 10.
+When the enemy is near death, you deal a huge spike.
+```
+
+**Berserk (limited charges)** — 3 charges only, but each one hits hard.
+
+```text
+new DamageAbility(40, 3) — triggers ~63% each turn (budget ×1.67), 3 uses total.
+```
+
+---
+
+## HealAbility
+
+Restores HP to the attacker each time it triggers.
+
+**Steady regeneration** — modest heal every few turns.
+
+```text
+new HealAbility(15) — heals 15 HP, triggers 80% of the time.
+```
+
+**Desperate surge** — heals more when critically low.
+
+```text
+activate() returns 40 if attacker.hp.current < 20, else 8.
+Nearly dead? Panic-heal for a large burst.
+```
+
+**Vampiric strike** — heals based on how hard you attack.
+
+```text
+activate() returns attacker.attackPower / 2.
+Steals life proportional to your own strength.
+```
+
+---
+
+## ArmorAbility
+
+Reduces the opponent's `attackPower` permanently for the rest of the bout.
+
+**Steady debuff** — grinds down the enemy's attack over time.
+
+```text
+new ArmorAbility(5) — reduces opponent attack by 5, triggers 100% of the time.
+By turn 4 the enemy hits much weaker.
+```
+
+**Intimidation (one shot)** — big one-time armor shred on first contact.
+
+```text
+new ArmorAbility(20, 1) — 1 charge, reduces attack by 20 (100% guaranteed, budget ×5).
+One scary moment early that sets the tone for the whole fight.
+```
+
+---
+
+## Ability + onTakeDamage (ability swap)
+
+The most advanced pattern — your monster **changes ability mid-fight** when hurt.
+
+**Cornered animal** — starts defensive, switches to offense when badly hurt.
+
+```text
+Start with a HealAbility.
+In onTakeDamage(): when HP drops below 50%, swap this.ability to a DamageAbility.
+describe() says "Cornered, X fights back with desperation!"
+```
+
+**Growing counter** — tracks damage taken and scales the ability accordingly.
+
+```text
+this.hitsReceived = 0
+onTakeDamage() increments hitsReceived
+activate() returns hitsReceived × 3
+The ability grows stronger the longer the fight goes on.
+```
diff --git a/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/README.md b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/README.md
new file mode 100644
index 00000000..a5ceef56
--- /dev/null
+++ b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/README.md
@@ -0,0 +1,156 @@
+# Monster Arena ⚔️
+
+_Created by Paolo Bozzini for HackYourFuture Denmark — modified fork from [PaoloBozzini/oop-monster-arena](https://github.com/PaoloBozzini/oop-monster-arena/tree/main)._
+
+Each group builds a custom monster. At the end of the session, **all monsters fight in a round-robin tournament** — every monster vs every other monster, once. The arena animates each battle in real time in the browser, with a live results matrix and a 1 000-simulation Monte Carlo win-rate chart.
+
+---
+
+## What you'll build
+
+A JavaScript class that extends `Monster`. Your monster has:
+
+- A **name**, **health points**, and **attack power** (you choose the numbers)
+- A **special ability** — something creative that fires after each normal attack
+
+The base `Monster` class uses a `HealthComponent` for HP management — this is an example of **composition** (has-a relationship). Your subclass is an example of **inheritance** (is-a relationship). You'll see both OOP patterns in action.
+
+**Stat budget:** your combined score — `health + attackPower × 3` — must be ≤ 300. Attack power is more expensive than raw HP (3 pts each vs 1 pt). Dragon uses 160 HP + 30 attack × 3 = 250 pts. Exceeding the budget throws an error immediately so you can fix it fast.
+
+---
+
+## Your workflow
+
+### 1. Get the project
+
+Clone or download this project, then create your own GitHub repository and push it there:
+
+```bash
+cd oop-monster-arena
+git remote set-url origin
+git push -u origin main
+npm install
+```
+
+### 2. Copy the template
+
+```bash
+cp src/monsters/your-monster.js src/monsters/YourMonsterName.js
+```
+
+Open the new file. Read `src/monsters/Dragon.js` for a full worked example, then fill in your stats and pick an ability to inject.
+
+### 3. Add your monster's image
+
+- Find or generate an image for your monster (Google Images, DALL·E, Midjourney…)
+- The filename **must exactly match your class name** (case-sensitive!)
+- If your image is a **PNG**: save it as `assets/monsters/YourMonsterName.png` — done!
+- If your image is a **JPG or other format**: save it with the right extension, then add this to your class:
+ ```js
+ get imagePath() { return 'assets/monsters/YourMonsterName.jpg'; }
+ ```
+
+### 4. Test your monster
+
+```bash
+npm test # tests src/monsters/your-monster.js
+npm test src/monsters/YourMonsterName.js # tests your renamed file
+```
+
+You should see green checks for all tests. Fix anything red before moving on.
+
+---
+
+## What you write
+
+Two classes in one file — an ability and a monster:
+
+```js
+// Your ability — extends one of the three base types
+class MyAbility extends DamageAbility {
+ activate(attacker, opponent) {
+ /* return damage amount */
+ }
+ describe(attacker, amount) {
+ /* return a log string */
+ }
+}
+
+// Your monster — extends Monster, injects your ability
+export class YourMonster extends Monster {
+ constructor() {
+ super("Name", health, attackPower, new MyAbility(amount));
+ }
+ onTakeDamage(amount) {
+ /* optional — react when hit, swap stats or ability */
+ }
+}
+```
+
+## Monster hooks
+
+| Override | When it's called | What to do |
+| ---------------------- | -------------------------- | -------------------------------------------- |
+| `onTakeDamage(amount)` | Every time you take damage | Change stats, swap ability — no return value |
+
+## Ability base types
+
+Extend one and override `activate()`. `triggerChance` is derived automatically as `budget / amount` — you never set it directly.
+
+| Base type | Budget | Default effect |
+| --------------- | ------ | ------------------------------------- |
+| `DamageAbility` | 15 | `opponent.takeDamage(this.amount)` |
+| `HealAbility` | 12 | `attacker.hp.heal(this.amount)` |
+| `ArmorAbility` | 8 | `opponent.attackPower -= this.amount` |
+
+Example: `new MyAbility(30)` extends `DamageAbility` → triggerChance = 15/30 = 50%.
+
+See `ABILITY_EXAMPLES.md` for full examples including the charge system.
+
+---
+
+## Study guide
+
+| File | Read it? | Edit it? |
+| ------------------------------ | -------------------------------------- | ------------------------ |
+| `src/core/health.js` | ✅ Yes — see how **composition** works | ❌ No |
+| `src/core/monster.js` | ✅ Yes — understand the base class | ❌ No |
+| `src/core/ability.js` | ✅ Yes — see the three ability types | ❌ No |
+| `src/monsters/Dragon.js` | ✅ Yes — your **reference example** | ❌ No |
+| `src/monsters/your-monster.js` | ✅ Yes | ✅ **This is your file** |
+| `src/arena.js` | Optional | ❌ No |
+| `src/ui.js` | Optional | ❌ No |
+
+---
+
+## Running the arena
+
+```bash
+npm install
+npm run dev
+```
+
+Open the URL shown in the terminal. To add more monsters to the tournament, update `src/main.js`:
+
+```js
+// Step 1: add an import at the top
+import { Hydra } from "./monsters/Hydra.js";
+import { Werewolf } from "./monsters/Werewolf.js";
+
+// Step 2: add a new instance to the array
+const monsters = [new Dragon(), new Hydra(), new Werewolf()];
+```
+
+Vite hot-reloads automatically — save `main.js` and the browser updates instantly.
+
+---
+
+## Checklist
+
+- [ ] Cloned/downloaded the project, created own repo, ran `npm install`
+- [ ] Copied and renamed `your-monster.js`
+- [ ] Renamed the class to match the filename (case-sensitive!)
+- [ ] Called `super()` with name, health, attack power, and an ability (budget: `health + attackPower × 3 ≤ 300`)
+- [ ] Passed a `DamageAbility`, `HealAbility`, or `ArmorAbility` as the 4th argument
+- [ ] Added an image to `assets/monsters/` (exact class name as filename)
+- [ ] Ran `npm test` — all checks pass
diff --git a/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/assets/monsters/.gitkeep b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/assets/monsters/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/assets/monsters/Dragon.svg b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/assets/monsters/Dragon.svg
new file mode 100644
index 00000000..b127eaa8
--- /dev/null
+++ b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/assets/monsters/Dragon.svg
@@ -0,0 +1,37 @@
+
diff --git a/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/assets/monsters/Goblin.svg b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/assets/monsters/Goblin.svg
new file mode 100644
index 00000000..c923c1ab
--- /dev/null
+++ b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/assets/monsters/Goblin.svg
@@ -0,0 +1,71 @@
+
diff --git a/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/assets/monsters/Troll.svg b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/assets/monsters/Troll.svg
new file mode 100644
index 00000000..d8578be1
--- /dev/null
+++ b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/assets/monsters/Troll.svg
@@ -0,0 +1,89 @@
+
diff --git a/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/index.html b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/index.html
new file mode 100644
index 00000000..7b6150ee
--- /dev/null
+++ b/courses/frontend/advanced-javascript/week4/session-materials/oop-monster-arena/index.html
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+ Monster Arena
+
+
+
+