Gå til indholdet

Opgavebeskrivelse: Dice War

Introduktion

Velkommen til opgaven Dice War! Målet med denne opgave er at bygge et simpelt konsolbaseret spil, hvor du kommer til at arbejde med klasser, nedarvning, arrays og delegates i C#. I Dice War skal en spiller og en computer kæmpe mod hinanden ved at kaste terninger, men terningerne er ikke helt almindelige: nogle af dem har særlige regler for pointgivning.

Denne opgave vil bevise, at du kan arbejde med følgende koncepter:

  • Arbejde i Visual Studio Code eller Visual Studio og med compiler og debugger.
  • Grundlæggende procedural C#-syntaks.
  • Arbejde med klasser.
  • Egenskaber og metoder i klasser.
  • Nedarvning til at udvide en klasses funktionalitet.
  • Benytte grundlæggende polymorfi.
  • Arrays for at oprette en samling af terninger.
  • Benytte NuGet til at installere pakker.

Tilmelding

For at kunne aflevere opgaven skal du tilmelde dig indtil jul 2024 på https://forms.gle/N6vWUCRpnZurGX439. Det er en Google Form og svar opbevares i Google Sheets. Når du er tilmeldt vil du modtage en mail med en OneDrive-folder hvor du kan uploade din løsning (se senere).

Jeg vil kun bruge dit navn og din mailadresse til at sende feedback og bevis på at du har løst opgaven.

Spilbeskrivelse

Dice War foregår i et antal runder. I hver runde kaster spilleren og computeren hver især et “bæger” med terninger. Terningerne i bægeret er specielle “krigsterninger”, hvor ulige tal tæller dobbelt. Efter hver runde får spilleren og computeren point baseret på summen af deres kastede terninger. Den samlede score for hver spiller opdateres, og spillet fortsætter, indtil alle runder er færdige.

Tip

Hvis du finder nogle fejl i beskrivelsen eller noget er uklart, så skriv til mig på michell@cronberg.dk.

Her er et eksempel på, hvordan output kan se ud (5 runder og 5 terninger i bægrene):

--- Round 1 ---
Player   rolled [5] [2] [3] [5] [1] a total of: 30
Computer rolled [5] [5] [2] [6] [6] a total of: 34

--- Round 2 ---
Player   rolled [5] [1] [2] [6] [3] a total of: 26
Computer rolled [4] [3] [5] [5] [2] a total of: 32

--- Round 3 ---
Player   rolled [6] [2] [6] [1] [4] a total of: 20
Computer rolled [4] [5] [5] [3] [6] a total of: 36

--- Round 4 ---
Player   rolled [1] [1] [2] [4] [2] a total of: 12
Computer rolled [6] [6] [3] [2] [1] a total of: 22

--- Round 5 ---
Player   rolled [4] [1] [1] [3] [5] a total of: 24
Computer rolled [3] [2] [3] [4] [1] a total of: 20

--- Game Over ---
Player   total points: 112
Computer total points: 144

Der er således ingen interaktion med spilleren, og spillet kører automatisk. Spillet er en simpel simulation, hvor spilleren og computeren kaster terninger og får point baseret på terningernes værdi.

Opgaveoversigt

Hele spillet er implementeret i en konsolapplikation - der er således blot et projekt i løsningen. Kald projektet DiceWar. Det skal være et .NET projekt men versionen er ikke kritisk (.NET 5 - .NET 9).

Du skal bygge fire klasser for at implementere spillet:

  • Dice: En standard terning.
  • WarDice: En speciel terning der arver fra Dice, hvor ulige tal tæller dobbelt.
  • WarDiceCup: Et bæger med flere terninger, som kan rulles samtidig.
  • Game: Styrer spillets flow og holder styr på point.

samt logik i Program.cs til at starte spillet.

Trin-for-trin Opgave

Trin 0: Projektet

  • Opret et nyt .NET-konsolapplikationsprojekt i Visual Studio Code eller Visual Studio.
  • Kald projektet DiceWar.
  • Tilføj NuGet-pakken PcgRandom til projektet for at kunne skabe “bedre” tilfældige tal.
    • Med “bedre” bevæger man sig ind i et nærmest religiøst område, hvor nogle mener, at de indbyggede tilfældige tal i .NET ikke er tilstrækkeligt tilfældige. Pakken PcgRandom er blot valgt for at bevise, at du kender til NuGet og kan installere en pakke.
  • Sørg for at projektet kan kompileres og køres med det normale “Hello World” output.

Trin 1: Dice-klassen

  • Opret en klasse Dice i namespacet DiceWar.
  • Dice skal have en Value-egenskab, som repræsenterer værdien af terningen.
    • Husk at en egenskab (property) er en måde at indkapsle et felt. En automatisk egenskab med en privat setter er fin til dette formål - men det er op til dig.
  • Implementer en Roll()-metode, som sætter Value til et tilfældigt tal mellem 1 og 6.
    • Husk at bruge pakken PcgRandom til at generere tilfældige tal (den fungerer som System.Random). Se deres dokumentation for at finde ud af, hvordan du bruger den.
  • Tilføj en Point()-metode, som returnerer værdien af Value.
    • Dette er jo blot en almindelig terning, så pointene er lig med værdien af terningen.
  • Tilføj ToString() for at vise værdien i en læsbar form, fx "[5]".
    • Det kan du gøre ved at overskrive ToString()-metoden i klassen (override). Se eventuelt i materialet - søg efter “Børn har sin egen implementation”
classDiagram
    class Dice {
        -int Value
        +void Roll()
        +int Point()
        +string ToString()
    }

Trin 2: WarDice-klassen

  • Opret en klasse WarDice, der nedarver fra Dice.
  • Overskriv Point()-metoden, så ulige tal tæller dobbelt, dvs. hvis Value er ulige, returneres Value * 2, ellers returneres Value.
    • Det er denne metode som er forskellig fra Dice-klassen.
classDiagram
    class Dice {
        -int Value
        +void Roll()
        +int Point()
        +string ToString()
    }

    class WarDice {
        +int Point()
    }
    Dice <|-- WarDice : inheritance

Trin 3: WarDiceCup-klassen

  • Opret en WarDiceCup-klasse
  • Implementer en konstruktør, som modtager antallet af terninger.
  • Tilføj et array af WarDice-objekter svarende til antallet af terninger.
  • Tilføj en RollAll()-metode, som ruller alle terninger i bægeret.
    • Den løber alle terningerne igennem og kalder Roll() på hver terning.
  • Implementer Point(), som summerer pointene fra alle terningerne i bægeret.
    • Her kan du blot løbe igennem alle terningerne og kalde Point() på hver terning.
  • Tilføj ToString() (override) for at returnere værdierne af alle terninger i bægeret som en enkelt streng, fx "[3] [5] [2] [6]".
classDiagram
    class DiceCup {
        -Dice[] _diceArray
        +DiceCup(int numberOfDices)
        +void RollAll()
        +int Point()
        +string ToString()
    }

Trin 4: Game-klassen

  • Opret en Game-klasse, som styrer spillet.
  • Game skal have to WarDiceCup-objekter – ét til spilleren og ét til computeren (se klassediagrammet).
  • Der skal også være to private felter til at holde styr på pointene for spilleren og computeren (se klassediagrammet).
  • Implementer en PlayRound()-metode, som:
    • Ruller begge bægre.
    • Beregner og tilføjer point til spilleren og computeren.
    • Logger resultaterne for begge.
  • Implementer StartGame(int numberOfDices, int rounds), som kører flere runder med et antal terninger og udskriver slutresultatet.
classDiagram
    class Game {
        -DiceCup _playerCup
        -DiceCup _computerCup
        -int _playerPoint
        -int _computerPoint
        -int _rounds
        +Game(int numberOfDices, int rounds)
        +void PlayRound()
        +void StartGame()
    }

Info

Din løsning på denne opgave vil have flere “issues” i det virkelige liv - det er ikke super smart at være hårdt bundet til både en NuGet-pakke (hvad nu hvis “kunden” vil bruge noget andet) og Console.WriteLine (hvad nu hvis “kunde” vil kunne udskrive til en fil eller database eller …). Hvis du har lyst til at gøre noget ved det, så prøv at se på afkobling ved hjælp af eksempelvis interfaces og dependency injection eller delegates. Men det er ikke et krav for at løse opgaven.

Aflevering og bedømmelse

Aflevering af opgaven sker ved at uploade en ZIP-fil med hele projektet til en OneDrive-folder. Oplysningerne om denne folder vil blive givet når du tilmelder dig “eksamen”. Jeg vil ikke have /bin eller /obj mapperne med i zip-filen - kun de filer som er nødvendige for at kunne bygge og køre projektet (DiceWar.csproj, alle .cs filerne samt evt en sln-fil (solution)). Du må gerne inkludere en readme.txt hvor dit navn og mailadresse fremgår.

Det jeg gør for at tjekke om det virker er at jeg pakker det ud på min egen computer og prøver at bygge og køre det (dotnet run). Jeg vil også kigge på koden for at se om de forskellige klasser og metoder er implementeret som beskrevet i opgaven. Hvis alt er godt vil du bestå, ellers vil du få en mail med feedback om hvad der skal rettes.

Når opgaven er godkendt vil du modtage et bevis på at du har løst opgaven tilfredsstillende. Beviset vil indeholde en kort beskrivelse af opgaven.