2016. július 2., szombat

OOP félkész program.

Nyári gyakorlat.

Oop bevezető.

A tizedikeseknek eljött a nyári gyakorlat ideje, meleg is van, a tanulnivaló sem könnyű, arról nem is beszélve hogy - úgy látszik nem hiába -  szajkózom nekik egy ideje, hogy az MIT-n nem tanítanak OOP-t 2011 óta, a közeljövő a funkcionális nyelveké az üzemeltetés miatt, akkor meg minek erőltetem, nemdebár :). (Majd átírjuk ezt szépen ECMASCRIPT-be, nem kell izgulni ;-).)
Sebaj, mostanában úgyis divat az agilis fejlesztés, no meg tizenéves koromban az első kis programjaim egyike volt a kígyós játék - illetve területfoglalós volt tán, amit muszáj volt megírni, mert a kishúgomat érdekelte a suliból engedéllyel hazahozott gép, de csak ha lehet vele játszani -, úgyhogy az elméleti alapok ledarálása után nekiláttunk agilisen kígyós játékot fejleszteni.
A jelen állapot ebből kifolyóan még csak negyedig van készen, már működik ugyan, de ezer sebből vérzik.
Direkt.
Az adattagok nincsenek elrejtve, mindegyik publikus, a zártság egyébként sem teljesül, nincs szétszedve a projekt fájlokra, és még sorolhatnám, illetve sorolni is fogom. Az egyik feladata pont az volt a minap a tisztelt versenyzőknek, hogy pro, illetve kontra hozzanak fel példákat az elvek megvalósulására, illetve meg nem valósulására ebben a nyúlfarknyi programban. Mindenesetre működik már, és éppen ezért rögzítem le ezt az állapotot. Sajnos az idő elszaladt, így azt az állapotot amikor a már működő alkalmazást vizsgálva, ötleteltünk, hogy hogyan is kellene majd kinézni a program végső változatának, milyen funkciókat kell megvalósítani, et cetera (ami miatt jó dolog az agilis fejlesztés, ugye), nem fogja most követni tett, de ami késik, nem múlik.
Konzolos alkalmazás, monodevelop illetve visual studio segítségével fordítható linuxra vagy windowsra, ez alatt a két oprendszer alatt teszteltem, nem tudom hogy máshol fut-e.

Először a végeredmény :).

Nyitunk egy új konzolos alkalmazást, és egyszerűen bemásoljuk ami itt található.
Nincs kiírva hogy milyen billentyűkkel lehet vezérelni, ahhoz tanulmányozni kell a forrást :). (wsad, illetve a kurzorvezérlők két játékosnál,  lustáknak.)
Update: a böngésző lenyelte a List<coord> részt aljas módon, és ezt nem vettem észre eddig. Most már tényleg fut is a galád, ha bemásoljuk. Legalábbis Linux alatt biztos. Windows alatt lehet hogy a terminál mérete miatt elhasal, akkor azt csökkenteni kell. ( Console.SetWindowSize (100, 65); // beégetett érték, agilisek vagyunk, vagy mi :D?
vagy ezt a sort egyszerűen kiremelni és kész.) 

 
using System;
using System.Collections.Generic;

namespace kigyos
{
 public enum irany // felsorolás típus, olvashatóbb a kód, a gépi implementálása tömör
 {
  fel,
  le,
  jobbra,
  balra
 }
 public class coord//public class coord : obj Minden az object őse!!!
 {
  public int x;
  public int y;
  public coord(int x, int y) // konstruktor
  {
   this.x = x;
   this.y = y;
  }
  public override bool Equals(object obj) // példa a virtuális metódusokra
  {
   coord temp = obj as coord;
   return temp.x == this.x && temp.y == this.y;
  }
  public override int GetHashCode() // lusták vagyunk, rövid a lista, nem érdekes a hash annyira
  {
   return base.GetHashCode();
  }
 }
 public class alma // külön osztály az almáknak, lehetne máshogy is
 {
  public char KukacKaja; // a kaja karakteresen
  public coord aktxy; // ez már a javítás része, ha van coord típusunk, használjuk azt
  public int akty;
  public int aktx;
  public int KajaHossz;
  public alma(kigyo[] K, Random r) // ugye milyen jó lenne, ha zárt lenne az objektumunk? Nem kellene paramétereket dobálni
  {   almauj(K, r);   // itt kellene a coord-ot inicializálni :D
  }


  public void almauj(kigyo[] K, Random r)
  {

   this.KukacKaja = '*';
   bool siker = false;
   if (this.aktxy==null ) // nem itt kéne a coord initje, ha már...
    this.aktxy = new coord (0,0);
   do
   {


    aktx = r.Next(1, Console.WindowWidth - 1); // vigyázat! fixme a pálya nem biztos hogy az egész ablakra vonatkozik majd!
    akty = r.Next(1, Console.WindowHeight - 1);// mint fentebb
    aktxy.x=aktx; // javítás kezdete, majd kivezetjük az aktx, akty-t
    aktxy.y=akty;
    siker = true;
    for (int i = 0; i < K.Length; ++i)
    {
     if (K[i].kigyokoords.Contains(this.aktxy))
     {
      siker = false;
     }
    }


   } while (!siker);
   this.KajaHossz = r.Next(1, 4);
   this.KukacKaja = Convert.ToChar (this.KajaHossz+48); // 48=0, ascii!!
   Console.SetCursorPosition(this.aktxy.x, this.aktxy.y);
   Console.Write(this.KukacKaja);
  }
 }
 public class kigyo // kigyóosztály
 {
  public List<coord> kigyokoords; // a koordinátáink listája
  public irany aktirany; // merre nézünk
  public int akthossz; 
  public System.ConsoleColor kigyoszin;
  public char kigyojel;
  public bool utkozott;
  public int aktx; // ez is majd csere lesz a coord típusra
  public int akty;

  public int teteje; // tervezési hiba. az összes kígyóra vonatkozik, majd javítva lesz fixme
  public int alja;
  public int jobbSzel;
  public int balSzel;
  public ConsoleKey gomb_balra; // kell majd customizáló rész, ugye :D
  public ConsoleKey gomb_jobbra;
  public ConsoleKey gomb_fel;
  public ConsoleKey gomb_le;
  public int pont; // ez majd a játékoshoz köthető
  public kigyo(int melyik) // konstruktor hint: honnan is ismerjük fel c#-ban?
  {
   kigyokoords = new List<coord>(50); // helyet foglalunk neki, ne kelljen minden addnál. 
   switch (melyik) // ezért jó a partial class használata, az init feleslegesen foglalja itt a helyet, jobb lenne másik fájlban
   {
   case 1:
    this.aktirany = irany.jobbra;
    this.kigyoszin = System.ConsoleColor.Blue;
    this.kigyojel = 'O';
    this.aktx = 1;
    this.akty = 1;
    this.gomb_balra = ConsoleKey.A;
    this.gomb_jobbra = ConsoleKey.D;
    this.gomb_fel = ConsoleKey.W;
    this.gomb_le = ConsoleKey.S;
    break;
   case 2:
    this.aktirany = irany.balra;
    this.kigyoszin = System.ConsoleColor.Green;
    this.kigyojel = 'X';
    this.aktx = Console.WindowWidth - 1; // fixme, a pályaméret!
    this.akty = Console.WindowHeight - 1;
    this.gomb_balra = ConsoleKey.LeftArrow;
    this.gomb_jobbra = ConsoleKey.RightArrow;
    this.gomb_fel = ConsoleKey.UpArrow;
    this.gomb_le = ConsoleKey.DownArrow;
    break;
   case 3:
    this.aktirany = irany.le;
    this.kigyoszin = System.ConsoleColor.Yellow;
    this.kigyojel = 'M';
    this.aktx = Console.WindowWidth / 2;
    this.akty = 1;
    this.gomb_balra = ConsoleKey.G;
    this.gomb_jobbra = ConsoleKey.H;
    this.gomb_fel = ConsoleKey.T;
    this.gomb_le = ConsoleKey.F;
    break;
   case 4:
    this.aktirany = irany.fel;
    this.kigyoszin = System.ConsoleColor.Yellow;
    this.kigyojel = 'Y';
    this.aktx = Console.WindowWidth / 2;
    this.akty = Console.WindowHeight - 1;
    this.gomb_balra = ConsoleKey.K;
    this.gomb_jobbra = ConsoleKey.L;
    this.gomb_fel = ConsoleKey.I;
    this.gomb_le = ConsoleKey.J;
    break;
   default:
    this.aktirany = irany.fel;
    this.kigyoszin = System.ConsoleColor.Red;
    this.kigyojel = 'E';
    this.aktx = Console.WindowWidth / 2;
    this.akty = Console.WindowHeight / 2;
    this.gomb_balra = ConsoleKey.NumPad4;
    this.gomb_jobbra = ConsoleKey.NumPad6;
    this.gomb_fel = ConsoleKey.NumPad8;
    this.gomb_le = ConsoleKey.NumPad2;
    break;
   } // End Case 
   kigyokoords.Add(new coord(this.aktx, this.akty)); //
   this.akthossz = 5;
   this.utkozott = false;
   this.teteje = 0;
   this.alja = Console.WindowHeight;
   this.balSzel = 0;
   this.jobbSzel = Console.WindowWidth;
  } // End konstruktor
  public void possiblemove(ConsoleKey bill) // fontos metódus, ez állítja be a kígyó irányát
  {
   if (bill == this.gomb_balra && this.aktirany != irany.jobbra)
    this.aktirany = irany.balra;
   if (bill == this.gomb_jobbra && this.aktirany != irany.balra)
    this.aktirany = irany.jobbra;
   if (bill == this.gomb_fel && this.aktirany != irany.le)
    this.aktirany = irany.fel;
   if (bill == this.gomb_le && this.aktirany != irany.fel)
    this.aktirany = irany.le;

  }

  public void kigyomegy() // itt mozog a drága
  {
   char fejmutat=this.kigyojel;
   if (!this.utkozott)
   {
    // itt lehetne átírni a kígyó "fejét" a testére, ha lenne ilyen
    Console.SetCursorPosition (this.aktx, this.akty);
    Console.ForegroundColor = this.kigyoszin;
    Console.Write(this.kigyojel);
    switch (this.aktirany)
    {
    case irany.le:
     this.akty += 1;
     fejmutat='V';
     break;
    case irany.fel:
     this.akty -= 1;
     fejmutat='^';
     break;
    case irany.jobbra:
     this.aktx += 1;
     fejmutat='>';
     break;
    case irany.balra:
     this.aktx -= 1;
     fejmutat='<';
     break;
    }
    kigyokoords.Add(new coord(this.aktx, this.akty)); // a new foglalja a helyet, és csak a címe megy át az add-nak!
    if (this.akthossz <= kigyokoords.Count)
    {
     Console.SetCursorPosition(kigyokoords[0].x, kigyokoords[0].y);
     Console.Write(' ');
     kigyokoords.RemoveAt(0); //ha már elérte a hosszát, akkor az utolsó 8a listában első!!9 elem kuka 
    }
    Console.ForegroundColor = this.kigyoszin;
    if (this.akty <= this.teteje || this.akty >= this.alja || this.aktx <= this.balSzel || this.aktx >= this.jobbSzel) {
     this.utkozott = true;

    } else {
     Console.SetCursorPosition (this.aktx, this.akty);
     Console.Write(fejmutat);

    }

   }
  }

 }
 class MainClass
 {
  public static void Main(string[] args)
  {
   Random r = new Random(); // sajna csak pszeudorandomunk van, ezért a globális objektum
   ConsoleKey ch;
   DateTime starttime = DateTime.Now;
   Console.SetWindowSize (100, 65); // beégetett érték, agilisek vagyunk, vagy mi :D?
   Console.CursorVisible=false;
   int szam = 0;

   do
   {
    Console.WriteLine("Hányan szeretnének játszani(max5) ?");
    try // a kivételkezelés is téma, de csak itt szerepel most
    {
     szam = int.Parse(Console.ReadLine());
    }
    catch (Exception e)
    {
     Console.WriteLine("Kérem számot adjon  meg!! ");
     Console.WriteLine(e.Message);
    }

   } while (szam > 5 || szam < 1);
   int kszama = szam;
   int almaszama = 1 + kszama / 3; // kinek mennyi alma kell
   Console.Clear(); // persze, ki kellene rajzolni a pályát, de ...
   coord tempkoord = new coord(0, 0);
   alma[] almak = new alma[almaszama]; // egységbe zárás? vagy nem ;)?
   kigyo[] kigyok = new kigyo[kszama]; 
   for (int i = 0; i < kszama; i++) // kígyók init
    kigyok[i] = new kigyo(i + 1);
   for (int i = 0; i < almaszama; i++) // almák init
    almak[i] = new alma(kigyok, r);

   for (int i = 0; i < 200; ) // végtelen ciklus, de teszteléskor volt egy i++, így megállt
   {
    for (int j = 0; j < kszama; j++)
    {
     kigyok[j].kigyomegy();
     tempkoord.x = kigyok[j].aktx;
     tempkoord.y = kigyok[j].akty;
     for (int a = 0; a < almaszama; ++a)
     {
      if (almak[a].aktx == tempkoord.x && almak[a].akty == tempkoord.y) // ha megettük az almát :D
      {
       kigyok[j].akthossz += almak[a].KajaHossz; // adatelrejtés :)? Vagy nem ?
       almak[a].almauj(kigyok, r);
       //pont++;

      }
     }


    }
    for (int kigyok1 = 0; kigyok1 < kszama; kigyok1++)
    {
     tempkoord.x = kigyok[kigyok1].aktx;
     tempkoord.y = kigyok[kigyok1].akty;
     for (int kigyok2 = 0; kigyok2 < kszama; kigyok2++)
     {
      if (kigyok1 != kigyok2)
      {
       if (kigyok[kigyok2].kigyokoords.Contains(tempkoord)) // emiatt kellett az equals-t átírni a coord objektumban!!!!
       {
        kigyok[kigyok1].utkozott = true;
        //Console.WriteLine ("Ütközött {0}!!", kigyok1);
       }
      }
     }
    }
    starttime = DateTime.Now;
    while (DateTime.Now.Subtract(starttime).TotalMilliseconds < 200)// mágikus érték, fixme csak kétszáz millisecundum elteltével lép ki a billvizsgálatból
    {

     while (Console.KeyAvailable)/*Ha van leütött gomb, akkor lekérdez 
                                                 * if-fel is müködne*/
     {
      ch = Console.ReadKey(true).Key;// poliformizmusra példa a readkey(true)
      if (ch == ConsoleKey.Escape)
      {
       i = 300; // kilép a végtelen ciklusunkból escape esetén
      }
      bool vanmeg = false; // ha mindegyik kígyó ütközött, ez false marad
      for (int j = 0; j < kszama; j++)
       if (!kigyok[j].utkozott)
       {
        kigyok[j].possiblemove(ch);
        vanmeg = true; // ha csak egy is megy még, akkor igaz lesz
       };
      if (!vanmeg)
       i=300;
      
     }


    }

   }
   Console.WriteLine("Vége a játéknak!");
   for (int i = 0; i < kszama; i++) {
    Console.ForegroundColor = kigyok [i].kigyoszin;
    Console.WriteLine ("Az {0} ("+kigyok[i].kigyojel+") kelgyó hossza: {1}", i + 1, kigyok [i].akthossz);
   }
   Console.ReadLine();
  }
 }
}


Mint látható, van rajta mit javítani, de már játszható.


A fejlesztés "első futás" fázisa zárult, most következik a kódtisztítás, a következő futás előkészítése.
Párhuzamosan a kódtisztítás után refaktorálunk, átírjuk az egészet javascriptbe, majd azt is ideillesztem alkalmasint.

Közben történt egy pici javítás az élvezhetőség kedvéért, az itteni kódot is frissítettem.


Az eredeti célkitűzés.

Az elméleti alapok után....
(java és c# összehasonlítása, régi msdn cikk)

Készítsünk egy programot, ami a sokak által jól ismert "kígyós" játékot valósítja meg több játékossal, természetesen objektum orientáltan, úgy hogy minél előbb használatba vehető alkalmazásunk legyen.
A tesztelés miatt eltekintünk néhány szabálytól, egyelőre legyen minden publikus. (Majd megisszuk a levét, de nem probléma.)

Kezdésnek hozzunk létre egy koordináták objektumot, a kígyónkhoz ez úgyis szükséges lesz, mit ad ég a példában is ez szerepelt :D.

public class coord
{
 int x;
int y;


Ha összehasonlítjuk az eddigi késszel, akkor kicsit hiányos, de a kezdetekben ez bőven elég volt. Át tudtuk írni az értékét, és listát is készíthettünk belőle. Apropó lista. Milyen adatszerkezet jó a koordináták tárolására?
A kezdetekben a társaság a tömböt javasolta, de nem akartam elmenni ebbe az irányba, még ha volt is olyan példakód ahol tömbbel oldották meg a feladatot.
Akkor hogyan tanuljuk meg a listákat :)? Ami egyszerűbb is jóval, mint a tömböt ciklussal másolgatni, a méretét vizsgálni, hogy még beleférünk-e, et cetera, et cetera.
Ha nem írunk külön, akkor automatikusan a rendelkezésünkre áll egy konstruktor - ugyanez igaz a destruktorra is, természetesen -, úgyhogy a kezdetekben ez elégnek is tűnt.

A kígyó fejét külön is számon tartottuk - ezt nem ártott volna coord típussal elvégezni, ha már volt nekünk olyan -, kényelmes.
Ha már gyakorlottak lesznek a népek, a most várható senyvedés - a refaktorálás szépségei - miatt több odafigyelés igényelnek majd a tervezésnél, de jobb egy kis projektnél szembefutni a tipikus hibákkal szerintem. Ha hibának lehet nevezni, hogy objektumfa építés előtt implementálunk, tesztelünk. (Túl sok kód került ki a főprogramba, a tetejében fontos kódok. Ez nyilván nem maradhat úgy, pláne a következő lépcsőfoknál, amikor hálózatosra bővítjük ki a programot.)

-- Majd innen folytatom, most sleep :D.
Elég sokat aludtam közben :).

Az első teljesen kész objektumunk.

 Jó, csak majdnem :).
A koordináták nem véletlenül szerepelnek annyi tutorialban. A hagyományos (struktúrált) programozási nyelvekben nem "divat" gyárilag az ilyen összetettebb adatstruktúra. (Ínyencek persze tudják nagyon jól, hogy a 80-as évekbeli PL/1 (program language 1, egyes számú programozási nyelv, ahogy az IBM szerényen elnevezte ;)) tudott mátrix szorzást is, meg kezelte a komplex számokat is, de nem ez volt a jellemző, lássuk be.

 
public class coord//public class coord : obj Minden object őse!!!
 {
  public int x; // nincs még adatelrejtés!
  public int y; // majd később...
  public coord(int x, int y) // konstruktor
  {
   this.x = x;
   this.y = y;
  }
  public override bool Equals(object obj) // példa a virtuális metódusokra
  {
   coord temp = obj as coord;
   return temp.x == this.x && temp.y == this.y;
  }
  public override int GetHashCode() // lusták vagyunk, rövid a lista, nem érdekes a hash annyira
  {
   return base.GetHashCode();
  }
 }

Azért csak majdnem, mert továbbra sem rejtettük el az adatokat, mint említettem volt, ez később jön sorra. Létrehoztunk egy konstruktort, paraméterekkel. Figyeljük meg, hogy innentől kezdve sikít a fordító, ha paraméter nélkül akarunk létrehozni koordináta objektumot.

Miért van ott az a két metódus alatta?


Ezt nagyon fontos megérteni. Két objektum egyenlősége nem annyira triviális, mint ahogy azt mi gondolnánk. Vajon a 0.0000000000001 és a 0 egyenlőek? Attól függ, nemdebár. Sokan esnek/estek bele abba a csapdába, hogy nem veszik figyelembe a számábrázolás miatti eltéréseket. Ezen túl is számos olyan eset képzelhető el, amikor két "valamit" (objektumot) egyformának ítélünk meg, akkor is, ha a bennük tárolt értékek egyébként különbözőek.
Az objektumhierarchia előnyei itt mutatkoznak meg. Tudjuk hogy számos esetben szükségünk van arra, hogy két objektum egyezőségét megvizsgáljuk. Kedvünk persze nincs arra, hogy ezt minden esetben felüldefiniáljuk, de nem is kell. De lehetőségünk van rá.
Minden objektum közös őse az obj osztály.
Abban pedig van egy Equals metódus, ami felüldefiniálható. Ez most számunkra szükséges is, mert alapértelmezetten két objektum akkor azonos, ha szó szerint azonosak, vagyis ugyanazon a memóriatartományon helyezkedik el mindkettő. De nekünk az kell, hogy a két koordinátapár értékei egyezzenek meg.
A paraméterként megkapott - összehasonlítandó - objektumot coord típusúként nézve össze tudjuk hasonlítani az adott objektum (this) x és y koordinátáit a másikkal.

Oké, de mi a túró alatta a GetHashCode?


Képzeljük el, hogy van egy hatalmas termünk, polcokon telis tele adatkazettákkal, az egyszerűség kedvéért minden adatkazettán csak egy "objektummal". Valaki hoz egy új kazettát, kérdés, hogy már rendelkezünk-e egy ugyanolyannal. (Könyvek esetében ezért van szerző, cim, kiadó, kiadás éve, isbn szám, etc. alapján szortírozva minden könyv.) Nézzük végig az egész termet egyesével? Ugye ezt senki sem akarná. Erre való a hash code. Minden egyes kazettáról készítünk valamilyen eljárással (sok van!) egy rövid kivonatot. Csak ezeket a kivonatokat hasonlítjuk össze első körben, így drasztikusan le tudjuk csökkenteni a munkaigényes teljes összehasonlítást.
A mi esetünkben lehetünk lazák, az alapértelmezett hash kód nekünk megfelelő, nincs túl sok elem, ha kell nyugodtan végigmehet a programunk az egész listán, különben is hogyan tudnánk akkor bemutatni azt, hogy a felüldefiniált metódusban használjuk az ősobjektum eredeti metódusát. Ha valaki ügyes, kitalálhat jó hasht. Pl. a két koordináta szorzata jónak tűnik.
Az absztrakt metódusoknál ugyanez lenne a szisztéma, de akkor kötelező felüldefiniálni az absztrakt metódust. (Csúf lenne ha minden esetben meg kellene mondani, mit gondolunk egyenlőség alatt...)

Egy kis alapelmélet.



 
 public enum irany // felsorolás típus, olvashatóbb a kód, a gépi implementálása tömör
 {
  fel,
  le,
  jobbra,
  balra
 }


Említettem, hogy nem fukarkodunk a hibákkal :). "Rendes" programozó lehetőleg angol rövidítéseket használ. Nem biztos hogy magyar programozó olvassa majd a kódunkat, és a programírás alapnyelve az angol. Nekem sem tetszik ha spanyol nyelvű kommenteket kell értelmeznem, pedig elég sokan beszélnek spanyolul. A kínai kommentekről meg ne is beszéljünk ;).
Az irány helyett természetesen a direction lenne a helyes, és az up/down/right/left négyes.

Miért jó az enum (felsorolás) adattípus? 

Olvashatóbb és stabilabb a kódunk tőle, valamint kis helyen is elfér. Például ez elfér két biten akár, ha a fordítónak arra szottyanna kedve. Nem hinném, hogy adott esetben ez így lenne, de előfordulhat.
Fura, nem? Hosszabb a forráskódunk, de tömörebb, gyorsabb a legenerált kód. Jó szívvel ajánlom a programozóvá válni akaróknak, hogy legalább minimális szinten ismerkedjenek meg valamilyen assembly nyelvvel. Nem azt mondom, hogy gépi ciklusokat számolgassanak, meg önmódosító kódokkal bűvészkedjenek, mint mi annak idején a rettenetesen lassú processzorok idején, de nem árt ha tudjuk, mit is művel a processzorunk. (Már csak azért sem érdemes túlságosan belemélyedni a témába, mert a mai modern operációs rendszerek nagyon sok mindent nyújtanak a számunkra, amit azért macerás assembly-ből kezelni.)

Még egy bőbeszédű szerkezet.


 
    switch (this.aktirany)
    {
    case irany.le:
     this.akty += 1;
     fejmutat='V';
     break;
    case irany.fel:
     this.akty -= 1;
     fejmutat='^';
     break;
    case irany.jobbra:
     this.aktx += 1;
     fejmutat='>';
     break;
    case irany.balra:
     this.aktx -= 1;
     fejmutat='<';
     break;
    }


Miért találták ezt ki? Bőven elég erre az if is, miért bonyolítani az ember életét plusz vezérlési szerkezetekkel :)? Az egyik válasz értelemszerűen persze az, hogy így szebb, meg következetesebb a kódom. A switchnél megmondom hogy mit akarok összehasonlítani, nem kell külön gondolkodni rajta. De nem csak ezért hasznos ez. A programjainkban elég régóta nem használunk már ugró utasításokat, pedig a legenerált kódunk szinte másból sem áll. Ez a fajta szerkezet sokkal hatékonyabb kód generálását eredményezi, mintha ifekkel operálnánk. (Extrém esetben itt ki is használhatná a fordító, hogy a felsorolás típusunk elfér két biten.)
Vegyük észre, hogy a koordinátáinkat fix értékkel módosítjuk, nem lenne ez muszáj, ha grafikus felületen mozognának az objektumaink. Abban az esetben persze az Equals-nál nem lenne már elég a teljes azonosság! Ha két xy koordináta elég közel van egymáshoz, már egyenlőnek kellene tekintenünk. (gyök alatt ((x-x0)négyzet + (y-y0) négyzet)) kisebb mint a sugár lenne a korrekt  összehasonlítás ekkor.)

Jöjjön az almaobjektum.

Eredetileg ez készült el legutoljára, de mivel rövidebb, mint a kígyó, itt van a helye.

 
 public class alma // külön osztály az almáknak, lehetne máshogy is
 {
  public char KukacKaja; // a kaja karakteresen
  public coord aktxy; // ez már a javítás része, ha van coord típusunk, használjuk azt
  public int akty;
  public int aktx;
  public int KajaHossz;
  public alma(kigyo[] K, Random r) // ugye milyen jó lenne, ha zárt lenne az objektumunk? Nem kellene paramétereket dobálni
  {   almauj(K, r);   // itt kellene a coord-ot inicializálni :D
  }


  public void almauj(kigyo[] K, Random r)
  {

   this.KukacKaja = '*';
   bool siker = false;
   if (this.aktxy==null ) // nem itt kéne a coord initje, ha már...
    this.aktxy = new coord (0,0);
   do
   {


    aktx = r.Next(1, Console.WindowWidth - 1); // vigyázat! fixme a pálya nem biztos hogy az egész ablakra vonatkozik majd!
    akty = r.Next(1, Console.WindowHeight - 1);// mint fentebb
    aktxy.x=aktx; // javítás kezdete, majd kivezetjük az aktx, akty-t
    aktxy.y=akty;
    siker = true;
    for (int i = 0; i < K.Length; ++i)
    {
     if (K[i].kigyokoords.Contains(this.aktxy))
     {
      siker = false;
     }
    }


   } while (!siker);
   this.KajaHossz = r.Next(1, 4);
   this.KukacKaja = Convert.ToChar (this.KajaHossz+48); // 48=0, ascii!!
   Console.SetCursorPosition(this.aktxy.x, this.aktxy.y);
   Console.Write(this.KukacKaja);
  }
 }

Persze adatelrejtés itt sincs, no meg az almákat és a kígyókat kezelhetnénk egyben is akár, de ez legyen a jövő zenéje.
Sajnos minden lépést nem tudok itt bemutatni, mindenesetre eleinte nem az alma értéke reprezentálta az almát, hanem egy csillag, ez a rész ott árválkodik a kód elején.
Itt már nekikezdtünk a kód tisztításának, ha a koordinátákra van egy külön adattípusunk, akkor használjuk azt, nem?
Régi mondás, hogy oop program készítésénél nagyon fontossá válik az előzetes tervezés. Ha az ember ezt elmulasztja, akkor eléggé durván vissza tud ütni, mint ahogy azt ennél az objektumnál láthatjuk is.
Mivel agilisen fejlesztünk, időnként szükséges "menet közben kereket cserélni", erre példa az aktuális koordináta két megvalósítása, az eredetileg használt nemsokára eltűnik a kódból.
Súlyos hiba, hogy nem foglalkoztunk a zártsággal. (Abból a szempontból viszont, hogy így hamarabb volt futtatható kódunk, korántsem az. Ha kis, jól tesztelhető részeknél követünk el ilyen hibát, az nem feltétlenül akkora nagy gond, a lényeg hogy tudjunk róla, nem helyes amit teszünk, és javítsuk is ki.)
Emiatt paraméteként át kell vennünk a kígyókat tartalmazó tömböt, és a globális random objektumot.
A coord inicializálása sem szép, ha jól emlékszem órán ezt javítottuk is, de az a kód most nincs itt, én meg jó programozó lévén lusta vagyok most ezzel foglalkozni, no meg nem árt ezzel is tisztában lenni, ha egy objektum még nincs inicializálva, azt hogyan is veszem észre ;).
Az adattagjaink nem bonyolultak, az aktuális almát reprezentáló karakter (ne feledjük, lehet hogy újra kell majd rajzolnunk az almát bizonyos esetekben, bár ez is a jövő zenéje csak), az alma koordinátái, no meg az alma "értéke".
Ha létre akarunk hozni egy új almapozíciót, akkor meg kell vizsgáljuk, hogy nem egy kígyó belsejébe sikeredett-e. Érdemes megfigyelni, hogy jelen esetben előfordulhat hogy két alma ugyanott van, de ez nem okoz fennakadást.
A lista adatszerkezet segítségével tudjuk megvizsgálni ezt. A Contains metódus miatt kellett fentebb a coord objektumunk Equals metódusát felülírnunk. Ha nem tettük volna azt meg, akkor soha nem térne vissza igaz értékkel, mert hiába ugyanaz az x és y koordináta, az objektumok fizikailag nem azonosak.
Minden változónak, így az objektumoknak is van címe - ahol elhelyezkedik a memóriában, ha még nincs ilyen, akkor ez a null érték -, valamint értéke. A létrehozott objektumváltozó csak az objektum címét tartalmazza! Egy fizikailag létrehozott objektumra több objektumváltozó is hivatkozhat. Addig az objektumunk "él", ameddig van olyan változó, ami rá hivatkozik. Amennyiben már nincs ilyen, az úgynevezett szemétgyűjtő (garbage collection) szabadítja fel az objektumunk által elfoglalt helyet. Legalábbis a c# esetében és pár más hasonló oop nyelvben így van.

Akkor itt az idő nekiugrani a kígyónak :D.

Először az adattagokat nézzük át.


   public List<coord> kigyokoords; // a koordinátáink listája
  public irany aktirany; // merre nézünk
  public int akthossz; 
  public System.ConsoleColor kigyoszin;
  public char kigyojel;
  public bool utkozott;
  public int aktx; // ez is majd csere lesz a coord típusra
  public int akty;

  public int teteje; // tervezési hiba. az összes kígyóra vonatkozik, majd javítva lesz fixme
  public int alja;
  public int jobbSzel;
  public int balSzel;
  public ConsoleKey gomb_balra; // kell majd customizáló rész, ugye :D
  public ConsoleKey gomb_jobbra;
  public ConsoleKey gomb_fel;
  public ConsoleKey gomb_le;
  public int pont; // ez majd a játékoshoz köthető

Tudom, ismétlem magam, de itt sincsenek még az adatok elrejtve, fujj.
Már az első sorban elénk tűnik a lista.
Erről bővebben itt olvashatunk magyarul, ha valaki az eredeti leírást akarja angolul, a google a barátja ;).
Nekünk azért kényelmes ez a tároló típus, mert könnyen bővíthető, és a contains metódus is pont kapóra jön, valamint az utolsó elemet is fájdalommentesen - programírás szempotjából természetesen - könnyen törölhetjük. Tehát lesz majd egy listánk koordinátákból, ezt a listát kigyokoords-nak nevezzük el.
Az irányról már esett szó a felsorolásoknál, a kígyó aktuális hossza azért kell, hogy addig ne töröljük az utolsó elemet, ameddig ezt a hosszt el nem értük, a kígyó színe, és a kígyójel értelem szerű. Az ütközött logikai adattag is magáért beszél. Az aktuális x és y koordinátát külön is tároljuk. A pálya paraméterei jelenleg minden kígyónál külön szerepel, ez később természetesen változni fog, nem itt van a helye. A vezérlő gombokat sem kell külön magyarázni, esetleg a ConsoleKey típus lehet érdekes, érdemes utána nézni. A játékos pontszámát ebben a pillanatban még nem is használjuk.

Nézzük a kígyónk konstruktorát.
C#-ban egy objektum konstruktorát ellentétben más nyelvekkel nagyon egyszerűen definiáljuk, egyszerűen az objektum nevével megegyező nevű metódust, illetve különböző paraméterek esetén metódusokat hozunk létre. Ha ilyen nincs, alapértelmezetten készít nekünk egyet a rendszer. Az objektum megszüntetésére is készül egy alapértelmezett destruktor, ha mi akarunk ilyet létrehozni, akkor ~ jelet kell tenni a metódus neve elé.
        public kigyo(int melyik) // konstruktor hint: honnan is ismerjük fel c#-ban?
Jó lenne ha egy erőforrásfájlból szednénk az alapértékeket, ez is a későbbiekre vár egyelőre.
Amint ez jól látható, maximum öt kígyóra vagyunk felkészülve jelenleg. Ha az irányítást nem négy, hanem csak két gombbal kellene végezni (balra, jobbra), akkor egy billentyűzeten "elférne" több játékos is, természetesen. 


            kigyokoords = new List<coord>(50); // helyet foglalunk neki, ne kelljen minden addnál. 


Először is létrehozunk egy koordinátákat tartalmazó listát, aminek előre lefoglalunk ötven helyet.
Az inicializáló részből nézzük most a másodikat, hiszen ott van egy kis elrejtett hiba.
            case 2:
                this.aktirany = irany.balra;
                this.kigyoszin = System.ConsoleColor.Green;
                this.kigyojel = 'X';
                this.aktx = Console.WindowWidth - 1; // fixme, a pályaméret!
                this.akty = Console.WindowHeight - 1;
                this.gomb_balra = ConsoleKey.LeftArrow;
                this.gomb_jobbra = ConsoleKey.RightArrow;
                this.gomb_fel = ConsoleKey.UpArrow;
                this.gomb_le = ConsoleKey.DownArrow;
                break;



Bizony, "be van égetve" a pálya mérete. Ezen majd javítani illik. Itt látszik, hogy miért is választottuk a char típus helyett a Consolekey-t. Hálózatos működés esetén pl. kimondottan csak a kurzorvezérlőket fogjuk használni, azt meg a legkönnyebben így azonosíthatjuk. Ez a kódrészlet szerintem magáért beszél.

            kigyokoords.Add(new coord(this.aktx, this.akty)); //
            this.akthossz = 5;
            this.utkozott = false;
            this.teteje = 0;
            this.alja = Console.WindowHeight;
            this.balSzel = 0;
            this.jobbSzel = Console.WindowWidth;


Bármelyiket is választottuk az ötből, a fentiek mindegyikükre vonatkoznak. Innen is látszik, hogy a pályakoordináták szerencsétlen helyen vannak.

Két lényegi metódusunk van még, az egyik a kigyó aktuális irányát állítja be:

        public void possiblemove(ConsoleKey bill) // fontos metódus, ez állítja be a kígyó irányát
        {
            if (bill == this.gomb_balra && this.aktirany != irany.jobbra)
                this.aktirany = irany.balra;
            if (bill == this.gomb_jobbra && this.aktirany != irany.balra)
                this.aktirany = irany.jobbra;
            if (bill == this.gomb_fel && this.aktirany != irany.le)
                this.aktirany = irany.fel;
            if (bill == this.gomb_le && this.aktirany != irany.fel)
                this.aktirany = irany.le;

        }


Itt huncutkodtam egy kicsit, angol nevet adtam a metódusnak, persze nem possible_move lett a neve, annyira ne legyünk már "jók" :D. Ez szerintem teljesen egyértelmű.

Akkor jöjjön a lényeg, a mozgás.


        public void kigyomegy() // itt mozog a drága
        {
            char fejmutat=this.kigyojel;
            if (!this.utkozott)
            {


Bizonyos időközönként hívjuk meg ezt a metódust. Nyilván nem kell csinálni semmit, ha a kígyónk már ütközött.

                // itt lehetne átírni a kígyó "fejét" a testére, ha lenne ilyen
                Console.SetCursorPosition (this.aktx, this.akty);
                Console.ForegroundColor = this.kigyoszin;
                Console.Write(this.kigyojel);


Ahogy a megjegyzésben szerepel, itt lehetne átírni a kígyó fejét a testhez tartozó karakterre. Ezt meg is tesszük szépen. Még nem mozogtunk, az aktx és akty adattag az előző pozíciót őrzi.

                switch (this.aktirany)
                {
                case irany.le:
                    this.akty += 1;
                    fejmutat='V';
                    break;
                case irany.fel:
                    this.akty -= 1;
                    fejmutat='^';
                    break;
                case irany.jobbra:
                    this.aktx += 1;
                    fejmutat='>';
                    break;
                case irany.balra:
                    this.aktx -= 1;
                    fejmutat='<';
                    break;
                }


Ez értelemszerű, nem hinném hogy külön magyarázni kellene.

                kigyokoords.Add(new coord(this.aktx, this.akty)); // a new foglalja a helyet, és csak a címe megy át az add-nak!
                if (this.akthossz <= kigyokoords.Count)
                {
                    Console.SetCursorPosition(kigyokoords[0].x, kigyokoords[0].y);
                    Console.Write(' ');
                    kigyokoords.RemoveAt(0); //ha már elérte a hosszát, akkor az utolsó 8a listában első!!9 elem kuka 
                }


Hozzáadjuk a listához az aktuális elemet. Ha már elértük a hosszunkat, akkor az utolsó elemet töröljük, és a helyére szóközt írunk. Ezt a részt is javítottuk legutóbb, hiszen ha a kígyó belsejében van a vége, nem kell a szóköz írás. Ez legyen házi feladat, nem nehéz megoldani, még az is lehet hogy ideposztolom alkalmasint.

Jó, itt van.
                if (this.akthossz <= kigyokoords.Count)
                {
                    coord koord_temp = kigyokoords [0];
                    kigyokoords.RemoveAt(0); //ha már elérte a hosszát, akkor az utolsó 8a listában első!!9 elem kuka 
                    if (!kigyokoords.Contains(koord_temp) )
                    {
                        //Console.SetCursorPosition(kigyokoords[0].x, kigyokoords[0].y);
                        Console.SetCursorPosition(koord_temp.x, koord_temp.y);
                        Console.Write(' ');
                    }

                }


Előbb kitöröljük, aztán megnézzük, hogy a maradékban benne van-e.

                Console.ForegroundColor = this.kigyoszin;
                if (this.akty <= this.teteje || this.akty >= this.alja || this.aktx <= this.balSzel || this.aktx >= this.jobbSzel) {
                    this.utkozott = true;

                } else {
                    Console.SetCursorPosition (this.aktx, this.akty);
                    Console.Write(fejmutat);

                }


Ha a falnak mentünk, akkor beállítjuk az ütközött változót, ha nem, akkor kiírjuk az aktuális pozícióra az iránynak megfelelő jelet. Hint : a possible_move -nál ezt a jelet változtathatnánk alkalmasint.


Most már csak a főprogram maradt hátra, de ez ismételten a későbbiekre vár.

Addig is még egyszer a teljes kód, mert a fenti nem a legutolsó, ahogy elnéztem.


 
using System;
using System.Collections.Generic;

namespace kigyos
{
 public enum irany // felsorolás típus, olvashatóbb a kód, a gépi implementálása tömör
 {
  fel,
  le,
  jobbra,
  balra
 }
 public class coord//public class coord : obj Minden object őse!!!
 {
  public int x;
  public int y;
  public coord(int x, int y) // konstruktor
  {
   this.x = x;
   this.y = y;
  }
  public override bool Equals(object obj) // példa a virtuális metódusokra
  {
   coord temp = obj as coord;
   return temp.x == this.x && temp.y == this.y;
  }
  public override int GetHashCode() // lusták vagyunk, rövid a lista, nem érdekes a hash annyira
  {
   return base.GetHashCode();
  }
 }
 public class alma // külön osztály az almáknak, lehetne máshogy is
 {
  public char KukacKaja; // a kaja karakteresen
  public coord aktxy; // ez már a javítás része, ha van coord típusunk, használjuk azt
  public int akty;
  public int aktx;
  public int KajaHossz;
  public alma(kigyo[] K, Random r) // ugye milyen jó lenne, ha zárt lenne az objektumunk? Nem kellene paramétereket dobálni
  {   almauj(K, r);   // itt kellene a coord-ot inicializálni :D
  }


  public void almauj(kigyo[] K, Random r)
  {

   this.KukacKaja = '*';
   bool siker = false;
   if (this.aktxy==null ) // nem itt kéne a coord initje, ha már...
    this.aktxy = new coord (0,0);
   do
   {


    aktx = r.Next(1, Console.WindowWidth - 1); // vigyázat! fixme a pálya nem biztos hogy az egész ablakra vonatkozik majd!
    akty = r.Next(1, Console.WindowHeight - 1);// mint fentebb
    aktxy.x=aktx; // javítás kezdete, majd kivezetjük az aktx, akty-t
    aktxy.y=akty;
    siker = true;
    for (int i = 0; i < K.Length; ++i)
    {
     if (K[i].kigyokoords.Contains(this.aktxy))
     {
      siker = false;
     }
    }


   } while (!siker);
   this.KajaHossz = r.Next(1, 4);
   this.KukacKaja = Convert.ToChar (this.KajaHossz+48); // 48=0, ascii!!
   Console.SetCursorPosition(this.aktxy.x, this.aktxy.y);
   Console.Write(this.KukacKaja);
  }
 }
 public class kigyo // kigyóosztály
 {
  public List<coord> kigyokoords; // a koordinátáink listája
  public irany aktirany; // merre nézünk
  public int akthossz; 
  public System.ConsoleColor kigyoszin;
  public char kigyojel;
  public bool utkozott;
  public int aktx; // ez is majd csere lesz a coord típusra
  public int akty;

  public int teteje; // tervezési hiba. az összes kígyóra vonatkozik, majd javítva lesz fixme
  public int alja;
  public int jobbSzel;
  public int balSzel;
  public ConsoleKey gomb_balra; // kell majd customizáló rész, ugye :D
  public ConsoleKey gomb_jobbra;
  public ConsoleKey gomb_fel;
  public ConsoleKey gomb_le;
  public int pont; // ez majd a játékoshoz köthető
  public kigyo(int melyik) // konstruktor hint: honnan is ismerjük fel c#-ban?
  {
   kigyokoords = new List<coord>(50); // helyet foglalunk neki, ne kelljen minden addnál. 
   switch (melyik) // ezért jó a partial class használata, az init feleslegesen foglalja itt a helyet, jobb lenne másik fájlban
   {
   case 1:
    this.aktirany = irany.jobbra;
    this.kigyoszin = System.ConsoleColor.Blue;
    this.kigyojel = 'O';
    this.aktx = 1;
    this.akty = 1;
    this.gomb_balra = ConsoleKey.A;
    this.gomb_jobbra = ConsoleKey.D;
    this.gomb_fel = ConsoleKey.W;
    this.gomb_le = ConsoleKey.S;
    break;
   case 2:
    this.aktirany = irany.balra;
    this.kigyoszin = System.ConsoleColor.Green;
    this.kigyojel = 'X';
    this.aktx = Console.WindowWidth - 1; // fixme, a pályaméret!
    this.akty = Console.WindowHeight - 1;
    this.gomb_balra = ConsoleKey.LeftArrow;
    this.gomb_jobbra = ConsoleKey.RightArrow;
    this.gomb_fel = ConsoleKey.UpArrow;
    this.gomb_le = ConsoleKey.DownArrow;
    break;
   case 3:
    this.aktirany = irany.le;
    this.kigyoszin = System.ConsoleColor.Yellow;
    this.kigyojel = 'M';
    this.aktx = Console.WindowWidth / 2;
    this.akty = 1;
    this.gomb_balra = ConsoleKey.G;
    this.gomb_jobbra = ConsoleKey.H;
    this.gomb_fel = ConsoleKey.T;
    this.gomb_le = ConsoleKey.F;
    break;
   case 4:
    this.aktirany = irany.fel;
    this.kigyoszin = System.ConsoleColor.Yellow;
    this.kigyojel = 'Y';
    this.aktx = Console.WindowWidth / 2;
    this.akty = Console.WindowHeight - 1;
    this.gomb_balra = ConsoleKey.K;
    this.gomb_jobbra = ConsoleKey.L;
    this.gomb_fel = ConsoleKey.I;
    this.gomb_le = ConsoleKey.J;
    break;
   default:
    this.aktirany = irany.fel;
    this.kigyoszin = System.ConsoleColor.Red;
    this.kigyojel = 'E';
    this.aktx = Console.WindowWidth / 2;
    this.akty = Console.WindowHeight / 2;
    this.gomb_balra = ConsoleKey.NumPad4;
    this.gomb_jobbra = ConsoleKey.NumPad6;
    this.gomb_fel = ConsoleKey.NumPad8;
    this.gomb_le = ConsoleKey.NumPad2;
    break;
   } // End Case 
   kigyokoords.Add(new coord(this.aktx, this.akty)); //
   this.akthossz = 5;
   this.utkozott = false;
   this.teteje = 0;
   this.alja = Console.WindowHeight;
   this.balSzel = 0;
   this.jobbSzel = Console.WindowWidth;
  } // End konstruktor
  public void possiblemove(ConsoleKey bill) // fontos metódus, ez állítja be a kígyó irányát
  {
   if (bill == this.gomb_balra && this.aktirany != irany.jobbra)
    this.aktirany = irany.balra;
   if (bill == this.gomb_jobbra && this.aktirany != irany.balra)
    this.aktirany = irany.jobbra;
   if (bill == this.gomb_fel && this.aktirany != irany.le)
    this.aktirany = irany.fel;
   if (bill == this.gomb_le && this.aktirany != irany.fel)
    this.aktirany = irany.le;

  }

  public void kigyomegy() // itt mozog a drága
  {
   char fejmutat=this.kigyojel;
   if (!this.utkozott)
   {
    // itt lehetne átírni a kígyó "fejét" a testére, ha lenne ilyen
    Console.SetCursorPosition (this.aktx, this.akty);
    Console.ForegroundColor = this.kigyoszin;
    Console.Write(this.kigyojel);
    switch (this.aktirany)
    {
    case irany.le:
     this.akty += 1;
     fejmutat='V';
     break;
    case irany.fel:
     this.akty -= 1;
     fejmutat='^';
     break;
    case irany.jobbra:
     this.aktx += 1;
     fejmutat='>';
     break;
    case irany.balra:
     this.aktx -= 1;
     fejmutat='<';
     break;
    }
    kigyokoords.Add(new coord(this.aktx, this.akty)); // a new foglalja a helyet, és csak a címe megy át az add-nak!
    if (this.akthossz <= kigyokoords.Count)
    {
     coord koord_temp = kigyokoords [0];
     kigyokoords.RemoveAt(0); //ha már elérte a hosszát, akkor az utolsó 8a listában első!!9 elem kuka 
     if (!kigyokoords.Contains(koord_temp) )
     {
      //Console.SetCursorPosition(kigyokoords[0].x, kigyokoords[0].y);
      Console.SetCursorPosition(koord_temp.x, koord_temp.y);
      Console.Write(' ');
     }

    }
    Console.ForegroundColor = this.kigyoszin;
    if (this.akty <= this.teteje || this.akty >= this.alja || this.aktx <= this.balSzel || this.aktx >= this.jobbSzel) {
     this.utkozott = true;

    } else {
     Console.SetCursorPosition (this.aktx, this.akty);
     Console.Write(fejmutat);

    }

   }
  }

 }
 class MainClass
 {
  public static void Main(string[] args)
  {
   Random r = new Random(); // sajna csak pszeudorandomunk van, ezért a globális objektum
   ConsoleKey ch;
   DateTime starttime = DateTime.Now;
   Console.SetWindowSize (100, 65); // beégetett érték, agilisek vagyunk, vagy mi :D?
   Console.CursorVisible=false;
   int szam = 0;

   do
   {
    Console.WriteLine("Hányan szeretnének játszani(max5) ?");
    try // a kivételkezelés is téma, de csak itt szerepel most
    {
     szam = int.Parse(Console.ReadLine());
    }
    catch (Exception e)
    {
     Console.WriteLine("Kérem számot adjon  meg!! ");
     Console.WriteLine(e.Message);
    }

   } while (szam > 5 || szam < 1);
   int kszama = szam;
   int almaszama = 1 + kszama / 3; // kinek mennyi alma kell
   Console.Clear(); // persze, ki kellene rajzolni a pályát, de ...
   coord tempkoord = new coord(0, 0);
   alma[] almak = new alma[almaszama]; // egységbe zárás? vagy nem ;)?
   kigyo[] kigyok = new kigyo[kszama]; 
   for (int i = 0; i < kszama; i++) // kígyók init
    kigyok[i] = new kigyo(i + 1);
   for (int i = 0; i < almaszama; i++) // almák init
    almak[i] = new alma(kigyok, r);

   for (int i = 0; i < 200; ) // végtelen ciklus, de teszteléskor volt egy i++, így megállt
   {
    for (int j = 0; j < kszama; j++)
    {
     kigyok[j].kigyomegy();
     tempkoord.x = kigyok[j].aktx;
     tempkoord.y = kigyok[j].akty;
     for (int a = 0; a < almaszama; ++a)
     {
      if (almak[a].aktx == tempkoord.x && almak[a].akty == tempkoord.y) // ha megettük az almát :D
      {
       kigyok[j].akthossz += almak[a].KajaHossz; // adatelrejtés :)? Vagy nem ?
       almak[a].almauj(kigyok, r);
       //pont++;

      }
     }


    }
    for (int kigyok1 = 0; kigyok1 < kszama; kigyok1++)
    {
     tempkoord.x = kigyok[kigyok1].aktx;
     tempkoord.y = kigyok[kigyok1].akty;
     for (int kigyok2 = 0; kigyok2 < kszama; kigyok2++)
     {
      if (kigyok1 != kigyok2)
      {
       if (kigyok[kigyok2].kigyokoords.Contains(tempkoord)) // emiatt kellett az equals-t átírni a coord objektumban!!!!
       {
        kigyok[kigyok1].utkozott = true;
        //Console.WriteLine ("Ütközött {0}!!", kigyok1);
       }
      }
     }
    }
    starttime = DateTime.Now;
    while (DateTime.Now.Subtract(starttime).TotalMilliseconds < 200)// mágikus érték, fixme csak kétszáz millisecundum elteltével lép ki a billvizsgálatból
    {

     while (Console.KeyAvailable)/*Ha van leütött gomb, akkor lekérdez 
                                                 * if-fel is müködne*/
     {
      ch = Console.ReadKey(true).Key;// poliformizmusra példa a readkey(true)
      if (ch == ConsoleKey.Escape)
      {
       i = 300; // kilép a végtelen ciklusunkból escape esetén
      }
      bool vanmeg = false; // ha mindegyik kígyó ütközött, ez false marad
      for (int j = 0; j < kszama; j++)
       if (!kigyok[j].utkozott)
       {
        kigyok[j].possiblemove(ch);
        vanmeg = true; // ha csak egy is megy még, akkor igaz lesz
       };
      if (!vanmeg)
       i=300;
      
     }


    }

   }
   Console.WriteLine("Vége a játéknak!");
   for (int i = 0; i < kszama; i++) {
    Console.ForegroundColor = kigyok [i].kigyoszin;
    Console.WriteLine ("Az {0} ("+kigyok[i].kigyojel+") kelgyó hossza: {1}", i + 1, kigyok [i].akthossz);
   }
   Console.ReadLine();
  }
 }
}



Kipróbáltam, ez már lefut monodevelop alatt szépen :D.



Nincsenek megjegyzések:

Megjegyzés küldése