Selle projekti eesmärk oli luua funktsionaalne ussimäng, kasutades objektorienteeritud programmeerimise (OOP) põhiprintsiipe. Selle asemel, et kirjutada kogu kood ühte pikka faili, jagati programm iseseisvateks klassideks, millest igaühel on oma selge vastutusala (kapseldatus)
Esialgul lõin projekti Visual Stuidios ja nimetasin seda Madu

Lisasin kood mis oli juba antud ehk baasmäng




Siin on ekraanipilt mis saab pärast käivitamist

Nüüd arendame seda mängu edasi
esimeseks ma tegin raskusastmed ja Ekraani seaded
Eesmärk ja loogika
Selle klassi eesmärk on kaotada koodist “maagilised numbrid” (magic numbers). Klass kapseldab endasse mänguvälja mõõtmed ja kiiruse. Kasutades switch-konstruktsiooni, määratakse mängu parameetrid dünaamiliselt vastavalt mängija valitud raskusastmele. Mida raskem tase, seda väiksem on viivitus millisekundites (KiirusMS) ehk seda kiiremini uss liigub.
Siin on kood:
public class ManguSeaded
{
public int Laius { get; set; }
public int Kõrgus { get; set; }
public int KiirusMS { get; set; } // Viivitus millisekundites
public ManguSeaded(int tase)
{
// Raskusastme valik (1 - Lihtne, 2 - Keskmine, 3 - Raske)
switch (tase)
{
case 1:
KiirusMS = 200;
Laius = 40;
Kõrgus = 20;
break;
case 2:
KiirusMS = 100;
Laius = 30;
Kõrgus = 15;
break;
case 3:
KiirusMS = 50;
Laius = 20;
Kõrgus = 10;
break;
default:
KiirusMS = 150;
Laius = 40;
Kõrgus = 20;
break;
}
}
}
Muutusin ka programm cs-i et programmi käivitamisel tuleks ette ilus munüü ja taseme valik

Ja siin on tulemus

Selle asemel, et kontrollida ussi põrkumist konsooliakna äärega, lõin klassi Kaart. See loob List<Punkt> kollektsiooni, kuhu genereeritakse tsüklite abil # sümbolitest koosnevad välisseinad.
- OOP eelis: Kuna seinad on dünaamiline nimekiri punktidest, võimaldab see arhitektuur tulevikus luua erinevaid tasemeid, lisades
Takistusedlisti ka kaardi keskel asuvaid seinte elemente või labürinte.
using System;
using System.Collections.Generic;
public class Kaart
{
public List<Punkt> Takistused { get; private set; } = new List<Punkt>();
public Kaart(int laius, int kõrgus)
{
// Horisontaalsed välisseinad (ülemine ja alumine)
for (int x = 0; x < laius; x++)
{
Takistused.Add(new Punkt(x, 0, '#'));
Takistused.Add(new Punkt(x, kõrgus - 1, '#'));
}
// Vertikaalsed välisseinad (vasak ja parem)
for (int y = 0; y < kõrgus; y++)
{
Takistused.Add(new Punkt(0, y, '#'));
Takistused.Add(new Punkt(laius - 1, y, '#'));
}
}
public void Joonista()
{
foreach (var p in Takistused) p.Joonista();
}
}
Joonistasin ka seinaid

Siin on tulemus

Faili salvestamine ja Edetabel (Edetabel.cs)
Eesmärk ja loogika
Klass pakub andmete püsivust (data persistence). Tegemist on static klassiga, mis tähendab, et sellest ei looda isendit, vaid meetodeid kutsutakse otse välja.
Salvesta()meetod kirjutab andmed failiskoorid.txtkasutades formaatiNimi;Skoor.KuvaEdetabel()meetod kasutab päringuid, et andmed failist lugeda, tükeldada (Split), sorteerida kahanemise järjekorras (OrderByDescending) ja võtta funktsioonigaTake(5)ainult TOP 5 parimat tulemust.
public static class Edetabel
{
private static string failiTee = "skoorid.txt";
public static void Salvesta(string nimi, int skoor)
{
File.AppendAllLines(failiTee, new[] { $"{nimi};{skoor}" });
}
public static void KuvaEdetabel()
{
if (!File.Exists(failiTee)) return;
var skoorid = File.ReadAllLines(failiTee)
.Select(rida => rida.Split(';'))
.Where(osad => osad.Length == 2 && int.TryParse(osad[1], out _))
.Select(osad => new { Nimi = osad[0], Punktid = int.Parse(osad[1]) })
.OrderByDescending(x => x.Punktid)
.Take(5);
Console.WriteLine("\n--- TOP 5 EDETABEL ---");
foreach (var s in skoorid)
{
Console.WriteLine($"{s.Nimi}: {s.Punktid} punkti");
}
}
}
See küsib nime ja pärast salvestab tulemust

Mängu käivitamisel leidsin viga, et kõige raskemal tasemel uss on seinate väljase ehk parandasin seda

Siin on tulemus

heliefektid (Heliefektid.cs)
Eesmärk ja loogika
Heliefektide lisamiseks kasutatakse konsooli baasheli Console.Beep(sagedus, kestus). Kuna helisignaal on sünkroonne ja paneb programmi ootele, siis otse peatsüklis mängides hakkaks uss heli ajal hakkima. Probleemi lahendamiseks kasutasin asünkroonset meetodit Task.Run(), mis käivitab heli eraldi lõimes (thread), tagades mängu täieliku sujuvuse.
using System;
using System.Threading.Tasks;
public static class Heliefektid
{
public static void MängiSöömist()
{
// Käivitatakse taustal, et mäng ei jääks ootama
Task.Run(() => Console.Beep(800, 100));
}
public static void MängiKaotust()
{
Task.Run(() => {
Console.Beep(400, 150);
Console.Beep(300, 150);
Console.Beep(200, 300);
});
}
}
Mängureeglite täiendused: Enese hammustamine ja punktisüsteem
Mängitavuse tõstmiseks lisati funktsionaalsused otse põhiklassidesse:
Enese hammustamise kontroll (Uss.cs sees)
Uss kaotab mängu, kui tema pea koordinaadid kattuvad mõne tema kehaosa koordinaadiga. LINQ laiendusmeetod Skip(1) jätab kontrollist välja pea enda (listi esimene element) ja Any() vaatab, kas mõni ülejäänud punkt ühtib peaga.
public bool KasHammustasEnnast()
{
Punkt pea = HangiPea();
return keha.Skip(1).Any(p => p.X == pea.X && p.Y == pea.Y);
}
Dünaamiline punktisüsteem (Program.cs sees)
Loodi muutuja int skoor = 0;. Iga kord, kui peatsükkel tuvastab, et pea asukoht kattub toidu asukohaga, suurendatakse skoori 10 punkti võrra ja väljastatakse see reaalajas mänguvälja allääres.
// Loogika peatsükli sees toidu söömisel:
if (pea.X == toit.Asukoht.X && pea.Y == toit.Asukoht.Y)
{
skoor += 10;
uss.Kasva();
toit.LooUusToit();
Heliefektid.MängiSöömist();
}
// Skoori kuvamine reaalajas kaardi all:
Console.SetCursorPosition(0, seaded.Kõrgus + 1);
Console.Write($"Skoor: {skoor}");
Siin on rohkem pilte mängu väljanägemiseks:

Raskuse valik

Mäng ise


Erinevad raskustasemed


Mängu lõpp ja Salvestamis küsimus nimeks

Ja edetabel
github link: https://github.com/MihkelUnt2006/Madu/tree/master/Madu
