Objektorienterad Programmering - Bygg Mini Battle Arena ⚔️
I detta projekt får ni träna på OOP-relaterade koncept som ännu inte täcks av webserverprogrammeringen med SvelteKit & Prisma.
Ditt uppdrag 🎯
Bemästra objektorienterad programmering genom att bygga en CLI battle arena:
- Förstå klasser och objekt genom Fighter-system
- Implementera arv med olika fighter-typer (Warrior, Mage, Archer)
- Skapa interfaces för battle behaviors
- Hantera inkapsling för stats och health
- Bygga generisk Arena för tournaments
Mål: Skapa ett fungerande battle system där olika fighters kan mötas i epic duels!
mkdir battle-arena
cd battle-arena
npm init -y
npm install -D typescript @types/node ts-node
npx tsc --init
Kör ditt spel med: npx ts-node arena.ts
Fas 1: Grundläggande Fighter-klass 🥊
Steg 1: Analysera vad en fighter behöver
Tänk innan du kodar:
// Vad behöver alla fighters ha gemensamt?
// - Namn för att identifiera dem
// - Health för att veta när de förlorar
// - Attack power för skada
// - Någon form av special ability?
// Din uppgift 1: Lista 5 properties som alla fighters bör ha
// 1.
// 2.
// 3.
// 4.
// 5.
Steg 2: Skapa bas Fighter-klassen
Din första klass med utmaningar:
// arena.ts
class Fighter {
// Din uppgift 2: Deklarera properties
// Tips: name (string), health (number), maxHealth (number), attackPower (number)
constructor(name: string, health: number, attackPower: number) {
// Din uppgift 3: Initialisera alla properties
// Kom ihåg att sätta maxHealth = health
}
// Din uppgift 4: Implementera attack() metoden
// Ska returnera en string som beskriver attacken
// Använd fighter's namn i meddelandet
attack(): string {
// Din kod här
}
// Din uppgift 5: Implementera takeDamage()
// Parameter: damage (number)
// Minska health med damage
// Health kan aldrig gå under 0
// Returnera true om fightern fortfarande lever, false om död
takeDamage(damage: number): boolean {
// Din logik här
}
// Din uppgift 6: Implementera isAlive()
// Returnera true om health > 0
isAlive(): boolean {
// Din kod här
}
// Din uppgift 7: Implementera getStatus()
// Returnera string med namn och health, ex: "Warrior Bob: 75/100 HP"
getStatus(): string {
// Din kod här
}
}
Steg 3: Testa din bas-klass
Verifiera att allt fungerar:
// Din uppgift 8: Skapa två fighters och testa alla metoder
// Skapa fighter1 och fighter2
// Låt dem attackera varandra
// Kolla status efter varje attack
// Test kod här:
console.log("Första testet klart!");
Steg 4: Basic battle loop
Enkel stridsmekanism:
// Din uppgift 9: Implementera en enkel battle funktion
// Parametrar: fighter1 och fighter2
// Loop tills någon dör
// Alternera vem som attackerar
// Skriv ut vad som händer varje runda
// Returnera vinnaren
function battle(fighter1: Fighter, fighter2: Fighter): Fighter {
console.log(`🥊 BATTLE BEGINS: ${fighter1.getStatus()} vs ${fighter2.getStatus()}`);
let round = 1;
// Din logik här - while loop som fortsätter tills någon dör
// Tips: använd fighter1.isAlive() && fighter2.isAlive()
// Vem vann?
}
// Din uppgift 10: Testa battle funktionen
// Skapa fighters och låt dem slåss
Fas 2: Interfaces för Battle Behaviors 🎭
Steg 1: Identifiera olika beteenden
Tänk på vad fighters kan göra:
// Vilka olika "abilities" kan fighters ha?
// - Vissa kan försvara sig (block/shield)
// - Vissa kan heal
// - Vissa har special attacks
// - Vissa kan buff/debuff
// Din uppgift 11: Vilka interfaces behöver vi?
// Föreslå 3 interfaces med deras metoder:
// Interface 1:
// Metod:
// Interface 2:
// Metod:
// Interface 3:
// Metod:
Steg 2: Definiera interfaces
Skapa kontrakt för beteenden:
// Din uppgift 12: Definiera Defensive interface
// Metod: defend() som returnerar number (damage reduction)
interface Defensive {
// Din kod här
}
// Din uppgift 13: Definiera Magical interface
// Metod: castSpell() som returnerar string (spell description)
// Metod: getMana() som returnerar number
interface Magical {
// Din kod här
}
// Din uppgift 14: Definiera Ranged interface
// Metod: rangedAttack() som returnerar number (damage)
// Metod: getAmmo() som returnerar number
interface Ranged {
// Din kod här
}
Steg 3: Testa interface thinking
Planera innan implementering:
// Din uppgift 15: Vilka fighter-typer ska implementera vilka interfaces?
//
// Warrior: implementerar Defensive (kan blockera)
// Mage: implementerar Magical (kan casta spells)
// Archer: implementerar Ranged (kan skjuta pilar)
//
// Skriv ner dina tankar om varför denna kombination gör sense:
/*
*/
Fas 3: Specialiserade Fighter-typer 🛡️🔮🏹
Steg 1: Warrior klassen
Den defensiva fightern:
class Warrior extends Fighter implements Defensive {
// Din uppgift 16: Lägg till armor property
constructor(name: string, health: number = 120, attackPower: number = 25, armor: number = 10) {
// Din uppgift 17: Anropa super() och sätt armor
}
// Din uppgift 18: Override attack() metoden
// Warriors gör mer damage: attackPower + (armor / 2)
// Returnera beskrivande meddelande
attack(): string {
// Din kod här - beräkna total damage och returnera meddelande
}
// Din uppgift 19: Implementera defend() från Defensive interface
// Returnera armor värde som damage reduction
defend(): number {
// Din kod här
}
// Din uppgift 20: Skapa getDamageReduction() metod
// Beräkna hur mycket damage som blockeras baserat på armor
getDamageReduction(incomingDamage: number): number {
// Din logik - t.ex. armor reducerar damage med armor/2 men max 50%
}
}
Steg 2: Mage klassen
Den magiska fightern:
class Mage extends Fighter implements Magical {
// Din uppgift 21: Lägg till mana property
constructor(name: string, health: number = 80, attackPower: number = 15, mana: number = 100) {
// Din uppgift 22: Implementera constructor
}
// Din uppgift 23: Override attack()
// Mages gör magic damage som kostar mana
// Om mana < 10: gör weak physical attack
// Om mana >= 10: gör strong magic attack och minska mana
attack(): string {
// Din kod här
}
// Din uppgift 24: Implementera castSpell()
// Kostar 30 mana, gör extra damage (attackPower * 2)
// Returnera spell description eller "Not enough mana"
castSpell(): string {
// Din kod här
}
// Din uppgift 25: Implementera getMana()
getMana(): number {
}
// Din uppgift 26: Skapa healSelf() metod
// Kostar 20 mana, healar 30 HP (up to maxHealth)
healSelf(): string {
// Din kod här
}
}
Steg 3: Archer klassen
Den långdistans fightern:
class Archer extends Fighter implements Ranged {
// Din uppgift 27: Lägg till arrows property
constructor(name: string, health: number = 100, attackPower: number = 20, arrows: number = 30) {
// Din uppgift 28: Implementera constructor
}
// Din uppgift 29: Override attack()
// Om arrows > 0: använd pil och gör normal damage
// Om arrows = 0: melee attack med reducerad damage (attackPower / 2)
attack(): string {
// Din kod här
}
// Din uppgift 30: Implementera rangedAttack()
// Kostar 2 arrows, gör extra damage (attackPower * 1.5)
// Returnera damage eller 0 om inte enough arrows
rangedAttack(): number {
// Din kod här
}
// Din uppgift 31: Implementera getAmmo()
getAmmo(): number {
}
}
Steg 4: Polymorfism test
Testa att alla typer fungerar som Fighter:
// Din uppgift 32: Skapa array med olika fighter-typer
const fighters: Fighter[] = [
// Din kod - skapa en av varje typ
];
// Din uppgift 33: Loop genom alla och visa polymorfism
// Anropa attack() för alla och se olika beteenden
console.log("=== POLYMORFISM TEST ===");
fighters.forEach(fighter => {
// Din kod här
});
// Din uppgift 34: Type checking challenge
// Loop igen men använd instanceof för att anropa specifika metoder
// Exempel: endast Warriors kan defend(), endast Mages kan castSpell()
console.log("=== SPECIAL ABILITIES ===");
fighters.forEach(fighter => {
// Din kod med instanceof checks
});
Fas 4: Inkapsling och Stats Management 🔒
Steg 1: Problem med nuvarande design
Vad kan gå fel?
// Vad är problemet med denna kod?
const warrior = new Warrior("Conan", 100, 25, 10);
warrior.health = 999999; // Cheat!
warrior.attackPower = -50; // Nonsense!
// Din uppgift 35: Lista 3 problem med öppen data access
// 1.
// 2.
// 3.
Steg 2: Säker Fighter bas-klass
Refactorera med proper inkapsling:
class Fighter {
private _name: string;
private _health: number;
private _maxHealth: number;
private _attackPower: number;
constructor(name: string, health: number, attackPower: number) {
// Din uppgift 36: Validera input och sätt private fields
// Namn får inte vara tomt
// Health och attackPower måste vara positiva
}
// Din uppgift 37: Skapa getters (read-only access)
get name(): string { }
get health(): number { }
get maxHealth(): number { }
get attackPower(): number { }
// Din uppgift 38: Skapa protected setter för health
// (endast subklasser ska kunna ändra)
// Validera: inte negativt, inte över maxHealth
protected setHealth(value: number): void {
// Din validering här
}
// Din uppgift 39: Uppdatera takeDamage() för att använda private fields
takeDamage(damage: number): boolean {
// Använd setHealth() för validering
}
// Din uppgift 40: Skapa healthPercentage getter
get healthPercentage(): number {
// Returnera health som procent (0-100)
}
// Uppdatera andra metoder för att använda getters...
}
Steg 3: Säker Warrior implementation
Uppdatera för inkapsling:
class Warrior extends Fighter implements Defensive {
private _armor: number;
constructor(name: string, health: number = 120, attackPower: number = 25, armor: number = 10) {
// Din uppgift 41: Validera armor (måste vara positivt)
// Anropa super med validerade värden
}
get armor(): number {
}
// Din uppgift 42: Säker armor upgrade metod
upgradeArmor(points: number): void {
// Validera att points är positivt
// Max armor = 50
// Skriv ut resultat
}
// Uppdatera andra metoder för nya getters...
}
Steg 4: Stats validation
Testa din säkerhet:
// Din uppgift 43: Försök bryta ditt system
const testWarrior = new Warrior("Test", 100, 20, 15);
// Testa ogiltiga värden:
// testWarrior.health = -50; // Ska inte fungera längre!
// Testa edge cases:
// Skriv ut vad som fungerade/inte fungerade:
Fas 5: Generisk Arena och Tournament System 🏟️
Steg 1: Planera Arena klassen
Vad ska en arena kunna göra?
// Din uppgift 44: Designa Arena funktionalitet
// Vad behöver en arena hantera?
// - Lista av fighters
// - Tournaments (flera battles)
// - Statistik (wins/losses)
// - Different tournament types?
// Skriv ditt design här:
/*
*/
Steg 2: Generisk Arena implementation
Flexibel arena för alla fighter-typer:
// Din uppgift 45: Skapa generisk Arena klass
class Arena<T extends Fighter> {
private fighters: T[] = [];
private battleHistory: /* vilken typ? */ = [];
constructor(private name: string) {
}
// Din uppgift 46: Implementera addFighter()
// Validera att fighter inte redan finns
addFighter(fighter: T): boolean {
// Din kod här - check för duplicates baserat på namn
}
// Din uppgift 47: Implementera removeFighter()
removeFighter(name: string): boolean {
// Din kod här
}
// Din uppgift 48: Implementera listFighters()
listFighters(): void {
// Skriv ut alla fighters med deras status
}
// Din uppgift 49: Implementera randomBattle()
// Välj 2 random fighters och låt dem slåss
// Spara resultatet i battleHistory
randomBattle(): string | null {
// Din kod här
}
// Din uppgift 50: Implementera tournament()
// Round-robin: alla slåss mot alla
// Returnera vinnare (mest wins)
tournament(): T | null {
// Din kod här - detta är en utmaning!
}
}
Steg 3: Battle Statistics
Spåra fighter prestanda:
// Din uppgift 51: Skapa FighterStats klass
class FighterStats {
// Vad vill vi spåra?
// wins, losses, totalDamageDealt, totalDamageTaken, battlesPlayed
constructor(public fighterName: string) {
}
// Din uppgift 52: Implementera update metoder
recordWin(): void { }
recordLoss(): void { }
recordDamage(dealt: number, taken: number): void { }
// Din uppgift 53: Implementera getWinRate()
getWinRate(): number {
// Returnera win percentage
}
// Din uppgift 54: Implementera getSummary()
getSummary(): string {
// Returnera formaterad statistics string
}
}
// Din uppgift 55: Integrera stats i Arena
// Uppdatera Arena för att spåra fighter statistics
Fas 6: CLI Interface och Komplett System 💻
Steg 1: Command Parser
Hantera user input:
// Din uppgift 56: Implementera command parsing
class CommandParser {
// Kommandon vi vill stödja:
// "create warrior Bob" - skapa fighter
// "list" - visa alla fighters
// "battle Alice Bob" - specifik battle
// "random" - random battle
// "tournament" - kör tournament
// "stats Alice" - visa fighter stats
// "quit" - avsluta
static parseCommand(input: string): { command: string; args: string[] } {
// Din kod här - split input och returnera structured data
}
}
Steg 2: Game Controller
Huvudlogik för CLI:
// Din uppgift 57: Implementera GameController
class GameController {
private arena: Arena<Fighter>;
private running: boolean = true;
constructor() {
this.arena = new Arena<Fighter>("Epic Battle Arena");
// Lägg till några default fighters för testing?
}
// Din uppgift 58: Implementera handleCommand()
handleCommand(command: string, args: string[]): void {
switch (command) {
case 'create':
// Din kod - parse fighter type och name, skapa fighter
break;
case 'list':
// Din kod
break;
case 'battle':
// Din kod - hitta fighters och starta battle
break;
case 'random':
// Din kod
break;
case 'tournament':
// Din kod
break;
case 'stats':
// Din kod
break;
case 'quit':
// Din kod
break;
default:
console.log("Unknown command. Try: create, list, battle, random, tournament, stats, quit");
}
}
// Din uppgift 59: Implementera createFighter()
private createFighter(type: string, name: string): Fighter | null {
// Din kod - factory pattern för att skapa rätt fighter type
}
// Din uppgift 60: Implementera main game loop
async run(): Promise<void> {
console.log("🏟️ Welcome to Epic Battle Arena! 🏟️");
console.log("Commands: create <type> <name>, list, battle <name1> <name2>, random, tournament, stats <name>, quit");
// Import readline för user input
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
while (this.running) {
// Din kod för input loop
}
}
}
Steg 3: Huvudprogram
Starta allt:
// Din uppgift 61: Skapa main function och starta spelet
async function main() {
const game = new GameController();
await game.run();
}
// Din uppgift 62: Error handling
// Wrap main() i try-catch för graceful errors
if (require.main === module) {
// Din kod här
}
Steg 4: Testing och polish
Slutlig validering:
// Din uppgift 63: Skriv test scenarios
// Testa alla commands:
// 1. Skapa fighters av olika typer
// 2. Lista fighters
// 3. Kör battles
// 4. Kör tournament
// 5. Kolla stats
// 6. Edge cases (ogiltiga commands, non-existent fighters, etc.)
// Dokumentera dina test results:
/*
*/
Avslutande Utmaningar 🏆
Om du vill gå längre:
Performance Challenge:
// Din uppgift 64: Optimera för stora tournaments
// Vad händer med 1000 fighters?
// Hur kan du optimera tournament() metoden?
Feature Extensions:
// Din uppgift 65: Lägg till nya features
// - Fighter levels och experience
// - Equipment system (weapons/armor upgrades)
// - Special tournament modes (elimination, team battles)
// - Save/load fighters till fil
Design Patterns:
// Din uppgift 66: Implementera nya patterns
// - Observer: UI updates när battles händer
// - Strategy: Olika AI behaviors för fighters
// - Factory: Mer sofistikerad fighter creation
Reflektion 🤔
Efter implementering, svara på:
Teknisk förståelse:
-
Vilka OOP-principer hjälpte mest med code organization?
-
Vad var svårast med generics och varför?
-
Hur hjälpte interfaces dig att designa flexibla system?
Design insights:
-
Vilka trade-offs upptäckte du mellan enkelhet och funktionalitet?
-
När valde du arv vs composition och varför?
-
Vad lärde dig mest om error handling och validation?
Grattis! 🎉
Du har nu byggt:
- ✅ Komplett CLI-applikation med OOP-principer
- ✅ Flexibel fighter-hierarki med arv och polymorfism
- ✅ Type-safe generisk arena system
- ✅ Robust error handling och data validation
- ✅ Interactive command interface för user interaction
Detta battle arena använder samma patterns som:
- Game engines (Unity, Unreal)
- Backend services (REST APIs)
- Desktop applications
- Mobile games
Genom att lösa varje utmaning steg för steg har du utvecklat skills för att tackla stora programmeringsprojekt!
Nästa steg: Använd dessa OOP-skills för webbutveckling med dina SvelteKit-lektioner!