Asist. univ. Laslo Eugen [622047]
UNIVERSITATEA DIN ORADEA
FACULTATEA DE ȘTIINȚE
PROGRAMUL DE ST UDIU INFORMATICĂ
FORMA DE ÎNVĂȚĂMÂNT ZI
Construcția unei aplicații ce
simulează jocul moara
Coordonatori științifici:
Conf. univ. dr . Căus V. Aurel
Asist. univ. Laslo Eugen
ORADEA
2015 Absolvent: [anonimizat] 1. Introducere ………………………….. ………………………….. ………………………….. ……… 3
1.1 Rezumat ………………………….. ………………………….. ………………………….. ……………………… 3
1.2 Propunerea aplicației ………………………….. ………………………….. ………………………….. ……. 3
1.3 Resursele aplicației ………………………….. ………………………….. ………………………….. ………. 3
1.4 Structura aplicației. Metoda MVVM. ………………………….. ………………………….. …………. 4
1.5 Reactive Extension ………………………….. ………………………….. ………………………….. ………. 7
Capitolul 2. Structura Programării Orientate pe Obiecte. Cuantificarea problemei …… 8
2.1 Construcția aplicației moara ………………………….. ………………………….. ………………………. 8
2.2 Tabla de joc ………………………….. ………………………….. ………………………….. ………………. 12
2.3 Verificarea moarei ………………………….. ………………………….. ………………………….. ……… 20
2.4 View -Model ………………………….. ………………………….. ………………………….. ……………… 24
Capitolul 3. Dezvoltarea proiectului din punct de vedere al utilizatorului ………………… 27
3.1 Pagina principală ………………………….. ………………………….. ………………………….. ……….. 27
3.2 Pagina de nivel ………………………….. ………………………….. ………………………….. ………….. 29
3.3 Pagina de opțiuni ………………………….. ………………………….. ………………………….. ……….. 34
3.4 Pagina de joc ………………………….. ………………………….. ………………………….. …………….. 36
3.5 Pagina de nume ………………………….. ………………………….. ………………………….. …………. 38
3.6 Pagina de jucături ………………………….. ………………………….. ………………………….. ………. 38
3.7 Pagina de câștigător ………………………….. ………………………….. ………………………….. ……. 39
3.8 Pagina de ajutor ………………………….. ………………………….. ………………………….. …………. 40
Capitolul 4. Utilizarea aplicației . ………………………….. ………………………….. ……………………. 41
Concluzii ………………………….. ………………………….. ………………………….. ………………………….. 41
Bibliografie ………………………….. ………………………….. ………………………….. ………………………. 42
Capitolul 1. Introducere
1.1 Rezumat
Această lucrare este constituită din 4 capitole. În primul capitol se prezintă propunerea și
structura aplicației de asemena și o serie de concepte general folosite în această lucrare. În
capitolul doi este prezentat modul de cuantificare a datelor și implementarea acestora . Capitolul
trei prezintă din punct de vedere al programatorului noțiuni care au fost folosite pentru a crea
design -ul si interfata acestei aplicații . Lucrarea se încheie cu un capitol destinat utilizatorului,
prin prezenatarea modului de utilizare a aplicației.
1.2 Propunerea aplicației
În această lucrare se dorește construirea unei o aplicații practice sub li mbajul de programare C#
ce simulează jocul moara cu următoarele facilită ți:
Dezvoltarea unui joc de logică pe diferite nivele de dificultate. În cazul în care
dorește, utilizatorul va putea să aleagă nivelul de dificultate. Fiecare nivel arată
diferit și n u în ultimul rând are un anumit număr de piese care pot fi folosite în joc.
Această aplicație poate fi folosită de două persoane. Doi jucători vor putea sa -și
introducă numele și fiecare jucător va fi informat, întru -n anumit mod, când
urmează.
Utilizatorul ap licației va ști la fiecare pas câte piese are, deoarece va fi afișat.
Iar la sfârșitul aplicației se va afișa numele jucătorului care a câștigat.
1.3 Resursele aplicației
În prima fază a construcției aplicației se vor cuantifica datele conform cerințelor propuse anterior.
Resursa aplicației va fi un fișier de tip rich text format, pentru butonul Help în care va conține
regulile aplicației :
“HelpPage.rtf ”. Pentru completarea acestui fișier a fost folosit o parte din sursa:
http://www.mastersgames.com/rules/morris -rules.htm
1.4 Structura aplicației. Metoda MVVM.
Modelul Model -View -ViewModel (MVVM) ajută la separarea prezentării logice de prezentarea
aplicației d e interfața de utilizator (UI ). De asemenea, poate îmbunătăți oportunitățile de
reutilizare a codului și permite dezvoltatorilor si designerilor de Interfața de utilizator să
colaboreze mai ușor în dezvoltarea aplicației. Folosind modelul MVVM, User Interface -ul este
separate de business logic fiind împărțit în trei clase distincte: View, care încapsulează UI și
logica UI; View Model, care încapsulează logică de prezentare și Model, care încapsulează logica
și datele. Prism include probe și implementări de referință, care arată cum să pună în aplicare
modelul MVVM într -o aplicație Windows Presentation Foundation (WPF).
View -ul interacționează cu View -Model -ul prin intermediul data binding, comenzi, și
evenimente de notif icare de schimbare. View Model -ul observă și coordonează actualizând
Model -ul. El convertește, valideazță datele necesare pentru afișarea în View. (Fig. 1.4. 1 –
MVVM)
Fig. 1.4.1 – MVVM
Sursa figurii “Fig. 1.4.1 – MVVM ” este: https://msdn.microsoft.com/en –
us/library/gg405484%28v=pandp.40%29.aspx
View -ului are responsabilitatea de a defini structura și aspectul a ceea ce utilizatorul vede pe
ecran. În mod ideal, cod din spatele unui view conține doar un constructor care solicită metoda
Initialize Component. View -ul este un element vizual, cum ar fi o fereastră, pagină, de control al
utilizatorului, sau template date. View -ul definește comenzile c onținute în view și aspectul lor
vizual cu stilul. View -ul se refera la view model prin proprietatea sa DataContext. Controalele
din ecranul sunt date legate de proprietățile și comenzile expuse de view model .
De exemplu, view -ul poate folosi convertoare de valoare pentru a formata datele care urmează să
fie afișate în interfața, sau poate folosi reguli de validare pentru a oferi validarea datelor de intrare
suplimentare pentru utilizator. View -ul definește și se ocupă de comporta mentul vizual al User
Interface -ului, cum ar fi animații care pot fi declanșate de o modificare în view model sau prin
interacțiune a utilizatorului cu UI.
View Model -ul (MVVM ) încapsulează logica de prezentare și datele pentru view. Nu are nici o
referire directă la view sau orice cunoștințe despre implementarea view -ului. View Model -ul
implementează proprietăți și comenzi la care v iew-ul poate lega date și trimite notifică ri view –
ului cu privire la modificările făcute prin organizarea de evenimente de notificare și
schimbare. Proprietățile și comenzi le pe care View Model -ul le oferă definește funcționalitatea
User Interface -ului, dar view -ul determină cum va fi folosită această funcționalitate.
View Model -ul este o c lasă non-vizual și nu derivă din nici o clasă de baza WPF. Acesta
încapsulează logica de prezentare. View Model -ul este independent de View și de Model . View
Model -ul de obicei nu se referiră în mod direct la View . Implementează proprietăți și comenzi la
care View -ul se poate lega prin data binding . Notifică view -ul cu privire la orice schimbare , cu
evenimentele de notificare și schimbare prin intermediul interfețelor INotifyPropertyChanged și
INotifyCollectionChanged. View Model -ul coordonează interacțiun ea View -ului cu Model –
ul. Poate să convertească sau să manipuleze date, astfel încât să poată fi ușor consumate de View
și poate aplica proprietăți suplimentare, care nu pot fi prezenți în Model . (Fig. 1.4.2 – Model
View ViewModel)
Fig. 1.4.2 – Model View ViewModel
Sursa figurii “Fig. 1.4.2 – Model View ViewModel ” este: https://msdn.microsoft.com/en –
us/library/ff798384.aspx
Data binding este procesul care stabilește o legătură dintre User Interface și business logic.Când
UI este schimbat, atunci datele care stau la bază vor reflecta această schimbare.
Binding markup extension o feră o valoare de proprietate, astfel încât valoarea este amânată până
în momentul în care va rula. O extensi e de marcare este transformat într -un obiect de exp resie
intermediar ă în XAML. Expresia și contextul datelor sunt folosite de motorul de legare
Silverlight pentru a determina valoarea proprietății la momentul execuției .
În cadrul XAML, orice valoare atribu t care este înconjurat în acolade { …} este o extensie de
marcare. În timp ce cele mai multe dintre markup -ul XAML sunt folosit e pentru a construi
controale și panouri, pentru a asambla arborele vizual al cererii, extinderea markup injecta
funcționalități s uplimentare în XAML. Legarea Markup Extension construiește un binding și
asociază cu UIElement sau FrameworkElement ca proprietatea la care aparț ine. Toate legăturile
au o proprietate obiect sursă, proprietate sursă, obiect țintă și țintă .(Fig. 1.4.3 – Binding)
Fig. 1.4.3 – Binding
Sursa figurii “Fig. 1.4.3 – Binding ” este: http://blog.scottlogic.com/2012/04/20/ev erything -you-
wanted -to-know -about -databinding -in-wpf-silverlight -and-wp7-part-two.html
Un exemplu în XML de binding :
Fig. 1.4.4 – Binding XAML
Sursa figurii “Fig. 1.4.4 – Binding ” este: http://blog.scottlogic.com/2012/04/20/everything -you-
wanted -to-know -about -databinding -in-wpf-silverlight -and-wp7-part-two.html
1.5 Reactive Extension
Reactive User Interface este un MVVM framework care ne permite să accesăm Reactive
Extension pentru .NET ca să se creeze User Interfaces care rulează pentru orice platformă mobilă
sau desktop.
Reactive Extension este o bibliotecă pentru a compune programe asincrone și bazate pe
evenimente (event -base) utilizând secvențe observable și operatori de interogare LINQ.
Operațiunile Input/Output în mod normal sunt mai lente decât alte operațiuni.
Programarea sincronă este atunci când firele de execuție trebuie să aștepte până când operațiunile
I/O s -au terminat. Când firele de execuție se pot continua fără să aștepte ca operațiunile să fie
complete, spunem că firele de execuție pot efectua I/O asincron.
Utilizând Reactive E xtension putem reprezenta data streams asincrone, adică transfer de date la
viteză mare.
IReactiveCommnad implementează Reactive Commnad, care descriu acțiuni făcute în User
Interface ca și copy, open și ok.Metode Icommand sunt Can Execute și Execute. Can Execute
definește metoda care determină dacă comanda se poate executa în starea curentă.Iar Execute
definește metoda care va fi apelată când comanda este invocată.
Reactive Object este obiectul de bază pentru clasa View Model și implementează INotify
Prop erty Changed. Deci Reactive Object va face schimbări la nivel de observable pentru a
monitoriza obiectele care se schimbă.
Inotify Property Changed nu este o colecție, ci o interfață pentru obiectele din clase pentru a face
notificări Property Changed în cazul în care valuarea unei proprietăți se schimbă. Acesta ne
permite să invocăm evenimentul Property Changed de câte ori un obiect se schimbă. Adică se
adaugă, se scoate sau se modifică. Este compatibil cu următoarele colecții List <T>, Observable
Collect on<T>.
Observable Collection este o colec ție de date, de tip dinamic care oferă notificări utilizând
INotify Collection Changed când obiectele sunt modificate.Acest lucru se întâmplă din cauza că
se poate face data binding la un observable collection.
Capi tolul 2. Structura Programării Orientate pe Obiecte. Cuantificarea
problemei
2.1 Construcția aplicației moara
Se vor construi următoarele enumerări , care fac parte din clasa Enum.cs .
public enum PlaceStatus
{
Unoccupied,
P1,
P2
}
public enum GameState
{
Placing,
Moving,
Flying,
Over
}
Prima enumerare va fi Place Status care are urmatoarele componente:
Unoccupied, va fi variabila care care va fi folosită la verificarea locului
daca este ocupat
P1, va reprezenta jucătorul 1 ( Player 1 )
P2 va reprezenta jucătorul 2 ( Player 2 )
A doua e numerare numită GameState , va arata starea jocului:
Placing, va fi folosită la introducerea piselor în joc
Moving , va reprezenta a cea parte din joc în care piesele vor fi deplasate
Flying, variabila care va arăta dac ă jocul poate continua
Over, va arăta sfârșitul jocului .
Cu ajutorul următoarei clase vom modifica aspectul unui Place . În accest caz un Place va
reprezenta tabla de joc, mai exact punctele unde se vor putea plasa piesele.
public class Place : ReactiveObject
Această clasă derivată moștenește clasa de bază ReactiveObject din clasa
Reactive UI.ReactiveObject .
ReactiveObject este obiectul de bază pentru calsele View Model și implementează
INotifyPropertyChanged. INotifyPropertyChanged este o interfață folosită în clasele de obiecte,
care actualizează UI ( interfața de ut ilizator ) de câte ori un obiect este adăugat, șters sau
schimbat.
public static readonly Brush UnoccupiedColor = Brushes.White;
public static Brush P1Color
{
get { return Brushes.Aqua; }
}
public static Brush P2Color
{
get { return Brushes.BlueViolet; }
}
private Brush fillColor;
private int strokeThickness;
private PlaceStatus placeStatus;
public Place()
{
Status = PlaceStatus .Unoccupied;
StrokeThickness = 0;
}
public PlaceStatus Status
{
get { return placeStatus; }
set { OnPlacesStatusChanged( value); }
}
public Brush FillColor
{
get { return fillColor; }
set { this.RaiseAndSetIfChanged( ref fillColor, value); }
}
public int StrokeThickness
{
get { return strokeThickness; }
set { this.RaiseAndSetIfChanged( ref strokeThickness, value); }
}
public string PlaceName { get; set; }
public int Row { get; set; }
public int Column { get; set; }
public IEnumerable <Place> AdjacentPlaces { get; set; }
public void Highlight()
{
StrokeThickness = 5;
}
public void UnHighlight()
Se va construi o variabilă UnoccupiedColor de tip Brush folosit pentru a a umple cu culoarea alba
obiectul fillColor. În continuare cele doua metode P1Color si P2Color vor returna culoarea
albastra pentru piesa jucatorul ui P1 respectiv violet pentru piesa jucatorul ui P2.
Vom avea nevoie de următoarele variabil e:
private Brush fillColor, pentru a umple un tile
private int strokeThickness, grosimea unui tile, această variabilăa va fi
folositoare în momentul în care dorim sa mutăm o piesă
private Place Statu s, pentru a verifica dacă se poate pune o culoare sau se
poate pune o piesă în acel loc.
Metoda “OnPlace StatusChange ” va lua valarea care intră și va verifica pe rând cele 3 condiții.
Practic această metoda verifică daca tile -ul curent este neocupat, adică deocamdată nici un
jucator nu a plasat o piesă pe locul respectiv , atunci va fi colorată in alb. Dacă starea acestui loc,
este P1 se va colora in albastru. Asta înseamnă ca e râ ndul jucatorului numărul 1. Iar dacă starea
este P2 se va colora in violet, deoarece este rândul jucătorlui numărul 2.
public string PlaceName { get; set; }
public int Row { get; set; }
public int Column { get; set; }
public IEnumerable <Place> Adjacent Places { get; set; }
Aceste metode sunt proprietăți auto -implementate, care mai târziu vor fi folosite pentru a crea
diferitele nivele ale acestui joc.
Place Name, pentru fiecare loc unde se poate plasa o piesă {
StrokeThickness = 0;
}
private void OnPlacesStatusChanged( PlaceStatus value)
{
if (value == PlaceStatus .Unoccupied)
FillColor = UnoccupiedColor;
if (value == PlaceStatus .P1)
FillColor = P1Color;
if (value == PlaceStatus .P2)
FillColor = P2Color;
this.RaiseAndSetIfChanged( ref placeStatus, value);
}
Row, pentru linia pe care se vor afla punctele pe care se pot pune
piesele
Column, pentru coloana pe care se vor afla punctele pe care se pot
pune piesele.
De exemplu pentru pătratul cel mai mare in stânga sus, Place Name va fie egal cu
"OuterTopLeft" , Row va fie egal cu 0, iar Column va fi tot 0. Atunci primul tile va fi pe locul
[0][0].
Toate aceste lucruri vor fi posibile pentru că IEnumerable <Place > suportă o metodă de iterare, în
acest caz Place , putând alstfel sa creăm mai încolo un vector. ( Fi. 2.1 – Places )
Fig. 2.1 – Places
Proprietatea “Status” va citi variabila “ place Status” și îl va modifica în funcție de cerințele
metodei ” OnPlace StatusChanged ”, iar proprietățile “FillColor ” și “ StrokeThickness ” vor
modifica variabilele “fillColor” și “ strokeThickness ” care reprezintă culoarea și grosimea
marginii unei elipse în caz că undeva in cod s -a făcut o modificare . “RaiseAndSetIfChanged ” va
face ca modificarea să fie vizibilă și la nivel User Interface ( Interfața de utilizator ).
În clasa ”Game ” vom avea variabilele cu următoarele specificări:
winner, pentru jucătorul câștigător
state, pentru starea jocului, dacă jocul s -a terminat
places , dacă apar schimbări la nivel de places , adică locul unde se pot plasa piesele.
public class Games : ReactiveObject
{
private string winner = "";
private GameState state;
private ReactiveCollection <Place> places;
public Place CurrentlyMovingPiece { get; set; }
public Games()
{
state = GameState .Placing;
CreatePlaces();
}
public GameState State
{
get { return state; }
set {
if (value == GameState .Over)
OnGameOver( EventArgs .Empty);
this.RaiseAndSetIfChanged( ref state, value);
}
}
public ReactiveCollection <Place> Places
{
get { return places; }
set { this.RaiseAndSetIfChanged( ref places, value); }
}
public string Winner
{
get { return winner; }
set { this.RaiseAndSetIfChanged( ref winner, value); }
}
Fiecare proprietate din partea aceasta de cod vor salva modificările facute în caz ul în care s-a
produs vre -o schimbare.
2.2 Tabla de joc
Pe baza clasei ”Place ” vom crea țigle (tiles) pentru fiecare nivel în par te. Avem 3 nivele, deci
vom avem 3 cazuri . Fiecare caz în parte va conține o metodă asociată variabilei “ Places ” după
cum urmează:
protected void CreatePlaces()
{
try
{
switch (Properties. Settings .Default.Level)
{
case 1:
Places = Generate PlacesForLevel1();
break;
case 2:
Places = Generate PlacesForLevel2();
break;
case 3:
Places = Generate PlacesForLevel3();
break;
default:
break;
}
this.RaisePropertyChanged(x => x. Places);
}
catch (System. Exception ex)
{
}
}
Prin această parte de cod:
this.RaisePropertyChanged(x => x. Places);
Vom obține o clasă care poate fi legat direct la codul XAML utilizând Reactive Extension. Iar în
paranteze avem expresia care reprezint ă proprietatea “Places ”. Adică clasa care va putea fi legată
de codul XAML va fi cl asa “Places ”.
Prin metoda “ Generate Places ForLevel1 ” vom crea o variabilă “ Places ” de tipul var implicit
pentru primul nivel al jocului , în care vom instanția și vom crea noi tiles . Adică locurile unde vor
putea jucătorii sa plaseze piesele, ca în figura Fig. 2.1 –Places . Mai întâi vom crea tile -urile
pentru pătratul de dinafară, după care pentru pătratul din mijloc, iar în cele din urma pe cele din
interior.
ReactiveCollection <Place> GeneratePlacesForLevel1()
{
var Places = new ReactiveCollection <Place>(
new[]
{
// Outer
new Place { PlaceName = "OuterTopLeft" ,Row = 0, Column = 0 },
new Place { PlaceName = "OuterTopMiddle" , Row = 0, Column = 3 },
new Place { PlaceName = "OuterTopRight" , Row = 0, Column = 6 },
new Place { PlaceName = "OuterMiddle Left", Row = 3, Column = 0 },
new Place { PlaceName = "OuterMiddleRight" , Row = 3, Column = 6 },
new Place { PlaceName = "OuterBottomLeft" , Row = 6, Column = 0 },
new Place { PlaceName = "OuterBottomMiddle" , Row = 6, Column = 3},
new Place { PlaceName = "OuterBottomRight" , Row = 6, Column = 6 },
// Middle
new Place { PlaceName = "MiddleTopLeft" , Row = 1, Column = 1 },
new Place { PlaceName = "MiddleTopMiddle" , Row = 1, Column = 3 },
new Place { PlaceName = "MiddleTopRight" , Row = 1, Column = 5 },
new Place { PlaceName = "MiddleMiddleLeft" , Row = 3, Column = 1 },
new Place { PlaceName = "MiddleMiddleRight" , Row = 3, Column = 5 },
new Place { PlaceName = "MiddleBottomLeft" , Row = 5, Column = 1 },
new Place { PlaceName = "MiddleBottomMiddle" , Row = 5, Column =3 },
new Place { PlaceName = "MiddleBottomRight" , Row = 5, Column = 5 },
});
În continuare vom face conexiunile între places , deoarece moara se poate face numai dacă 3 țigle
sunt conectate sau 3 piese sunt unul lângă celălalt.
// Outer
Places[0].AdjacentPlaces = new[] { Places[1], Places[3] };
Places[1].AdjacentPlaces = new[] { Places[0], Places[2], Places[9] };
Places[2].AdjacentPlaces = new[] { Places[1], Places[4] };
Places[3].AdjacentPlaces = new[] { Places[0], Places[5], Places[11] };
Places[4].AdjacentPlaces = new[] { Places[2], Places[7], Places[12] };
Places[5].AdjacentPlaces = new[] { Places[3], Places[6] };
Places[6].AdjacentPlaces = new[] { Places[5], Places[7], Places[14] };
Places[7].AdjacentPlaces = new[] { Places[4], Places[6] };
// Middle
Places[8].AdjacentPlaces = new[] { Places[9], Places[11] };
Places[9].AdjacentPlaces = new[] { Places[1], Places[8], Places[10] };
Places[10].AdjacentPlaces = new[] { Places[9], Places[12] };
Places[11].AdjacentPlaces = new[] { Places[3], Places[8], Places[13]};
Places[12].AdjacentPlaces = new[] { Places[4], Places[10], Places[15]};
Places[13].AdjacentPlaces = new[] { Places[11], Places[14] };
Places[14].AdjacentPlaces = new[] { Places[6], Places[13], Places[1 5]};
Places[15].AdjacentPlaces = new[] { Places[12], Places[14] };
return Places; } }
Mai jos este un exemplu pentru “Places [0].Adjacent Places = new[] { Plaes [1], Places [3] }; ” ,
unde locurile numerotate sunt punctele unde se pot plasa piesele. În acest exemplu există o
conexiune între locul 0 și locul 1, și de asemenea între locul 0 și 3. (Fig. 2.2 – The connections)
Iar primul nivel vom avea conexiunile : (Fig. 2.3 – Level 1)
La fel se va proceda în toate celelalte cazuri . Pentru nivelul 2 vom avea cod ul:
private ReactiveCollection <Place> GeneratePlacesForLevel2()
{
var Places = new ReactiveCollection <Place>(
new[]
{
// Outer
new Place { PlaceName = "OuterTopLeft" ,Row = 0, Column = 0 },
new Place { PlaceName = "OuterTopMiddle" , Row = 0, Column = 3 },
new Place { PlaceName = "OuterTopRight" , Row = 0, Column = 6 },
new Place { PlaceName = "OuterMiddleLeft" , Row = 3, Column = 0 },
new Place { PlaceName = "OuterMiddleRight" , Row = 3, Co lumn = 6 },
Fig. 2.2 – The connections
Fig. 2.3 – Level 1
new Place { PlaceName = "OuterBottomLeft" , Row = 6, Column = 0 },
new Place { PlaceName = "OuterBottomMiddle" , Row = 6, Column = 3 },
new Place { PlaceName = "OuterBottomRight" , Row = 6, Column = 6 },
// Middle
new Place { PlaceName = "MiddleTopLeft" , Row = 1, Column = 1 },
new Place { PlaceName = "MiddleTopMiddle" , Row = 1, Column = 3 },
new Place { PlaceName = "MiddleTopRight" , Row = 1, Column = 5 },
new Place { PlaceName = "MiddleMiddleLeft" , Row = 3, Column = 1 },
new Place { PlaceName = "MiddleMiddleRight" , Row = 3, Column = 5 },
new Place { PlaceName = "MiddleBottomLeft" , Row = 5, Column = 1 },
new Place { PlaceName = "MiddleBottomM iddle", Row = 5, Column = 3 },
new Place { PlaceName = "MiddleBottomRight" , Row = 5, Column = 5 },
//Inner
new Place { PlaceName = "MiddleMiddleMiddle" , Row = 3, Column = 3},
});
// Make the connections
// Outer
Places[0].AdjacentPlaces = new[] { Places[1], Places[3] };
Places[1].AdjacentPlaces = new[] { Places[0], Places[2], Places[9] };
Places[2].AdjacentPlaces = new[] { Places[1], Places[4] };
Places[3].AdjacentPlaces = new[] { Places[0], Places[5], Places[11] };
Places[4].AdjacentPlaces = new[] { Places[2], Places[7], Places[12] };
Places[5].AdjacentPlaces = new[] { Places[3], Places[6] };
Places[6].AdjacentPlaces = new[] { Places[5], Places[7], Places[14] };
Places[7].AdjacentPlaces = new[] { Places[4], Places [6] };
// Middle
Places[8].AdjacentPlaces = new[] { Places[9], Places[11] };
Places[9].AdjacentPlaces = new[] { Places[1], Places[8], Places[10],
Places[16] };
Places[10].AdjacentPlaces = new[] { Places[9], Places[12] };
Places[11].AdjacentPlaces = new[] { Places[3], Places[8], Places[13],
Places[16] };
Places[12].AdjacentPlaces = new[] { Places[4], Places[10], Places[15],
Places[16] };
Places[13].AdjacentPlaces = new[] { Places[11], Places[14] };
Places[14].AdjacentPlaces = new[] { Places[6], Places[13], Places[15],
Places[16] };
Places[15].AdjacentPlaces = new[] { Places[12], Places[14] };
//Inner
Places[16].AdjacentPlaces = new[] { Places[9], Places[11], Places[12],
Places[14]};
return Places;
}
Și nivelul 2 vom avea conexiunile (Fig. 2.4 – Level 2):
Pentru nivelul 3 vom avea codul:
private ReactiveCollection <Place> GeneratePlacesForLevel3()
{
var Places = new ReactiveCollection <Place>(
new[]
{
// Outer
new Place { PlaceName = "OuterTopLeft" ,Row = 0, Column = 0 },
new Place { PlaceName = "OuterTopMiddle" , Row = 0, Column = 3 },
new Place { PlaceName = "OuterTopRight" , Row = 0, Column = 6 },
new Place { PlaceName = "OuterMiddleLeft" , Row = 3, Column = 0 },
new Place { PlaceName = "OuterMiddleRight" , Row = 3, Column = 6 },
new Place { PlaceName = "OuterBottomLeft" , Row = 6, Column = 0 },
new Place { PlaceName = "OuterBottomMiddle" , Row = 6, Column = 3 },
new Place { PlaceName = "OuterBottomRight" , Row = 6, Column = 6 },
// Middle
new Place { PlaceName = "MiddleTopLeft" , Row = 1, Column = 1 },
new Place { PlaceName = "MiddleTopMiddle" , Row = 1, Column = 3 },
new Place { PlaceName = "MiddleTopRight" , Row = 1, Column = 5 },
new Place { PlaceName = "MiddleMiddleLeft" , Row = 3, Column = 1 },
new Place { PlaceName = "MiddleMiddleRight" , Row = 3, Column = 5 },
new Place { PlaceName = "MiddleBottomLeft" , Row = 5, Column = 1 },
new Place { PlaceName = "MiddleBottomMiddle" , Row = 5, Column = 3 },
new Place { PlaceName = "MiddleBottomRight" , Row = 5, Column = 5 },
// Inner
new Place { PlaceName = "InnerTopLeft" , Row = 2, Column = 2 },
new Place { PlaceName = "InnerTopMiddle" , Row = 2, Column = 3 },
new Place { PlaceName = "InnerTopRight" , Row = 2, Column = 4 },
new Place { PlaceName = "InnerMiddleLeft" , Row = 3, Column = 2 },
new Place { PlaceName = "InnerMiddleRight" , Row = 3, Column = 4 },
new Place { PlaceName = "InnerBottomLeft" , Row = 4, Column = 2 },
new Place { PlaceName = "InnerBottomMiddle" , Row = 4, Column = 3 },
new Place { PlaceName = "InnerBottomRight" , Row = 4, Column = 4 }
Fig. 2.4 – Level 2
});
// Make the connections
// Outer
Places[0].AdjacentPlaces = new[] { Places[1], Places[3] };
Places[1].Adjace ntPlaces = new[] { Places[0], Places[2], Places[9] };
Places[2].AdjacentPlaces = new[] { Places[1], Places[4] };
Places[3].AdjacentPlaces = new[] { Places[0], Places[5], Places[11] };
Places[4].AdjacentPlaces = new[] { Places[2], Places[7], Places[12] };
Places[5].AdjacentPlaces = new[] { Places[3], Places[6] };
Places[6].AdjacentPlaces = new[] { Places[5], Places[7], Places[14] };
Places[7].AdjacentPlaces = new[] { Places[4], Places[6] };
// Middle
Places[8].AdjacentPlaces = new[] { Places[9], Places[11] };
Places[9].AdjacentPlaces = new[] { Places[1], Places[8], Places[10],
Places[17] };
Places[10].AdjacentPlaces = new[] { Places[9], Places[12] };
Places[11].AdjacentPlaces = new[] { Places[3], Places[8], Places[13],
Places[19] };
Places[12].AdjacentPlaces = new[] { Places[4], Places[10], Places[15],
Places[20] };
Places[13].AdjacentPlaces = new[] { Places[11], Places[14] };
Places[14].AdjacentPlaces = new[] { Places[6], Places[13], Places[15],
Places[22] };
Places[15].AdjacentPlaces = new[] { Places[12], P laces[14] };
// Outer
Places[16].AdjacentPlaces = new[] { Places[17], Places[19] };
Places[17].AdjacentPlaces = new[] { Places[9], Places[16], Places[18] };
Places[18].AdjacentPlaces = new[] { Places[17], Places[20] };
Places[19].AdjacentPlaces = new[] { Places[11], Places[16], Places[21] };
Places[20].AdjacentPlaces = new[] { Places[18], Places[12], Places[23] };
Places[21].AdjacentPlaces = new[] { Places[19], Places[22] };
Places[22].AdjacentPlaces = new[] { Places[14], Places[21], Places[23] };
Places[2 3].AdjacentPlaces = new[] { Places[20], Places[22] };
return Places;
}
Următoarea figura reprezintă cum va arăta nivelul 3 ( Fig. 2.5 – Level 3 ):
“GameOverEventHandler ” este un delegat care conține o referință la o metodă care nu întoarce o
valoare. Primul parametru a metodei este de tip Object și se referă la instanța care ridică
evenimentul. Al doilea parametru său este derivat din EventArgs tip și deține datele de
eveniment. Dacă evenimentul nu generează datele despre evenimente, al doilea parametru este
pur și simplu valoarea câmpului EventArgs.Empty. În caz contrar, al doilea parametru este un tip
derivat din EventArg s și furnizează orice câmpuri sau proprietăți necesare de a organiza datele de
eveniment ca în codul de mai jos :
“GameOver ” este un eveniment, care va conține notificări în caz că user -ul va face modificări.
public delegate void GameOverEventHandler (object sender, EventArgs e);
public event GameOverEventHandler GameOver;
Evenimentul “GameOver ” va fi fie nul, în cazul în care nici un client nu a fost legat de un delegat
la eveniment, sau se referă la un delegat care ar trebui să fie numit în cazul î n care evenimentul
este invocate, toate acestea întâmplându -se la sfârșitul jocului.
protected virtual void OnGameOver( EventArgs e)
{
if (GameOver != null)
GameOver( this, e);
}
Vom reține fiecare loc în parte a unei moare, într -o variabilă. La fel vom face și cu moara
curentă.
Fig. 2.5 – Level 3
public class Mill
{
public Place First { get; set; }
public Place Second { get; set; }
public Place Third { get; set; }
public int Turn { get; set; }
public Mill(Place first, Place second, Place third)
{
First = first;
Second = second;
Third = third;
}
public bool Equals(Mill other)
{
return First == other.First &&
Second == other.Sec ond &&
Third == other.Third;
}
}
2.3 Verificarea moarei
Clasa Player are următoarele variabile cu caracteristicile:
isPlayerTurn, care va arăta dacă e răndul jucătorului curent,
name, numele jucătorului
piecesLeft, piesle rămase în joc
backgroud, va fi folosit pentru schimbarea culorii numelui a jucătorului
previousMills, pentru reținerea moarei de dinaintea celui curent
piecesCanRemove, piesele care se pot muta, după ce toate piesele au fost
puse în joc
invisiblePieces, pe ntru piesele rămase care se pot pune în joc
InactiveColor, va reprezenta culoarea numelui jucătorului căruia nu îi este
rândul, culoarea va fi albă
ActiveColor, va reprezenta culoarea numelui jucătorului căruia îi este
rândul, culoarea va fi roșie
public class Player : ReactiveObject
{
private bool isPlayersTurn;
private string name;
private int piecesLeft;
private Brush background;
private ReactiveCollection <Mill> previousMills;
private int piecesCanRemove;
private int invisiblePieces;
public static readonly Brush InactiveColor = Brushes.White;
public static readonly Brush ActiveColor = Brushes.Red;
Pentru fiecare nivel în parte numărul de piese diferă. Acest lucru va fi evidențiat în switch -ul
următor:
public Player()
{
switch (Properties. Settings .Default.Level)
{
case 1:
Player1(5);
break;
case 2:
Player1(7);
break;
case 3:
Player1(9);
break;
}
}
Numerele din metoda apelată vor fi numărul de piese pentru fiecare nivel în parte . Cazul 1 va fi
nivelul 1 și așa mai departe.
Numerotarea piselor va începe de la 0, și va fi reținută în “PiecesLeft ”. În clipa în care va veni
rândul unui jucător, numele jucătorului se va face în roșu, altfel, dacă nu este rândul lui va fi alb.
La un moment dat când vom avea 3 piese aliniate una lângă alta, vom putea să luăm o piese de
adverar, de aceea vom avea nevoie de metoda ”PiecesCanRemove ”.
Variabilele ” invisiblePieces ” și ” piecesLeft ” vor reține numărul de pise ce pot fi puse în joc,
respective numărul de piese care sunt în joc.
private void Player1( int invisiblePieces)
{
InvisiblePieces = invisiblePieces;
PiecesLeft = 0;
previousMills = new ReactiveCollection <Mill>();
}
public string Name
{
get { return name; }
set { this.RaiseAndSetIfChanged( ref name, value); }
}
public bool IsPlayersTurn
{
get { return isPlayersTurn; }
set
{
this.RaiseAndSetIfChanged( ref isPlayersTurn, value);
Background = IsPlayersTurn ? ActiveColor : InactiveColor;
}
}
public int Turn { get; set; }
public Brush Background
{
get { return background; }
set { this.RaiseAndSetIfChanged( ref background, value); }
}
public int PiecesCanRemove
{
get { return piecesCanRemove; }
set { this.RaiseAndSetIfChanged( ref piecesCanRemove, value); }
}
public int InvisiblePieces
{
get { return invisiblePieces; }
set { this.RaiseAndSetIfChanged( ref invisiblePieces, value); }
}
public int PiecesLeft
{
get { return piecesLeft; }
set { this.RaiseAndSetIfChanged( ref piecesLeft, value); }
}
public ReactiveCollection <Mill> PreviousMills
{
get { return previousMills; }
set { this.RaiseAndSetIfChanged( ref previousMills, value); }
}
Se va verifica fiecare loc din moară și în funcție de nivelul ales, se vor verifica unde se poate face
moară. Pentru cazul 1 de mai jos avem acestă verificare la nivelul 1.
private void checkMill( int a, int b, int c, Place movedPlace, PlaceStatus
ps,ReactiveCollection <Place> places, ref Mill tofillplace)
{
if (places [a].Status == ts && places[b].Status == p s && places[c].Status
== ps&&
(movedPlace == places[a] ||
movedPlace == places[b] ||
movedPlace == places[c]))
tofillplace = new Mill(places[a], places[b], places[c]);
}
public bool AddNewMills( ReactiveCollection <Place> Places, PlaceStatus
ps,Place movedPlace)
{
try
{
switch (Properties. Settings .Default.Level)
{
case 1:
return AddNewMills1( places, p s, moved Places);
case 2:
return AddNewMills2( places, p s, moved Places);
case 3:
return AddNewMills3 (places, p s, moved Places);
default:
break;
}
}
catch (System. Exception ex)
{
}
return false;
}
Pentru nivelul 1 se face verificarea după metoda de mai sus, numerele reprezentând locurile unde
se pot face legăturile , “movedPlace ” va arăta dacăo piesă a fost mutată în alt loc, iar variabila
“places ” restul locurilor. Modificarea fiind salvată prin “ ref currentMill ”.
Adică în moara curentă se vor adăuga fiecare număr în parte care reprezintă locul.
În cazul în care moara curentă este null se va returna false.
private bool AddNewMills1( ReactiveCollection <Place> places, PlaceStatus ps,
Place movedPlace)
{
Mills currentMill = null;
checkMill(0, 1, 2, movedPlace, ps, places, ref currentMill);
checkMill(0, 3, 5, movedPlace, ps, places, ref currentMill);
checkMill(2, 4, 7, movedPlace, ps, places, ref currentMill);
checkMill(5, 6, 7, movedPlace, ps, places, ref currentMill);
checkMill(8, 9, 10, movedPlace, ps, places, ref currentMill);
checkMill(8, 11, 13, m ovedPlace, ps, places, ref currentMill);
checkMill(10, 12, 15, movedPlace, ps, places, ref currentMill);
checkMill(13, 14, 15, movedPlace, ps, places, ref currentMill);
// Check validity
if (currentMill == null) return false;
currentMill.Turn = Turn;
if (currentMill.First != movedPlace &&
currentMill.Second != movedPlace &&
currentMill.Third != movedPlace)
return false;
///////// // Board Pattern //////////
//
// [0] [1] [2]
// [8] [9] [10]
//
// [3] [11] [12] [4]
//
// [13] [14] [15]
// [5] [6] [7]
//
În continuare se va adăuga moara curentă la cea precedent prin:
PreviousMills.Add(currentMill);
La fel se va proceda și pentru celelalte 2 nivele. Diferenîța va fi “Board Pattern” -ul.
2.4 View -Model
În continuare vom avea următoarea clasă ” PlayersViewModel , care va conține un constructor
prin care vom seta ca primul jucător sa fie ” PlayerOne ” cu numele "Player 1"
public class PlayersViewModel : ReactiveObject
{
public PlayersViewModel()
{
PlayerOne = new Players { IsPlayersTurn = true, Name = "Player 1" };
PlayerTwo = new Players { IsPlayersTurn = false, Name = "Player 2" };
}
Se vor salva modificările facute la nivel de User Control.
private Players playerOne;
public Players PlayerOne
{
get { return playerOne; }
set { this.RaiseAndSetIfChanged( ref playerOne, value); }
}
private Players playerTwo;
public Players PlayerTwo
{
get { return playerTwo; }
set { this.RaiseAndSetIfChanged( ref playerTwo, value); }
}
În cazul în care “ PlayerOne.IsPlayersTurn ” are valuarea true, înseamnă că e rândul primul
jucător , iar dacă “PlayerTwo .IsPlayersTurn ” are valuarea true, înseamnă ca e rândul celui de -al
doilea jucător.
Se va evita ca la un jucător sa îi vină rândul de două ori, una după alta.
public void SwitchTurns()
{
if (PlayerOne.IsPlayersTurn)
{
PlayerOne.Turn++;
}
if (PlayerTwo.IsPlayersTurn)
{
PlayerTwo.Turn++;
}
PlayerOne.IsPlayersTurn = !PlayerOne.IsPlayersTurn;
PlayerTwo.IsPlayersTurn = !PlayerTwo.IsPlayersTurn;
}
Se va pune culoarea albă, ca și culoare de bază prin variabila “ DefaultPlaceFill ”, iar prin varibila
“Game ” vom instanția clasa “ Games ” și se vor face modificările prin
“this.RaiseAndSetIfChanged “.
public class GamesViewModel : ReactiveObject
{
private static readonly Brush DefaultPlaceFill = Brushes.White;
private Games game;
public GamesViewModel()
{
Game = new Games();
}
public Games Game
{
get { return game; }
set { this.RaiseAndSetIfChanged( ref game, value); }
}}
Vom face legătura între User Control și vom pune “ButtonPage ” ca fiind cea principlă.
public class WindowViewModel : ReactiveObject
{
private UserControl currentControl = new ButtonPage ();
public UserControl CurrentControl
{
get { return currentControl; }
set {this.RaiseAndSetIfChanged( ref currentControl, value); }
}
}
Vom instanția fiecare din clasele de mai sus dacă valuarea la fiecare variabilă în parte este null și
vom seta valuarea inițială după cum urmează :
public class ViewModel
{
private static GamesViewModel gameViewModel;
private static WindowViewModel mainWindowViewModel;
public static PlayersViewModel playerViewModel;
public static GamesViewModel GameViewModel
{
get
{
if (gameViewModel == null)
{
gameViewModel = new GamesViewModel ();
}
return gameViewModel;
}
set { gameViewModel = value; }
}
public static WindowView Model MainWindowViewModel
{
get
{
if (mainWindowViewModel == null)
{
mainWindowViewModel = new WindowViewModel ();
}
return mainWindowViewMod el;
}
set { mainWindowViewModel = value; }
}
public static PlayersViewModel PlayerViewModel
{
get
{
if (playerViewModel == null)
{
playerViewModel = new PlayersViewModel ();
}
return playerViewModel;
}
set { playerViewModel = value; }
}}
Obiectul de mai sus “ ViewModel ” aparține aplicației “ Application .Resources ” este folosit pentru
a transmite resurse prin Window și elementele aplicației. În plus, proprietatea resurselor este
inclusă în calea de căutare a resurselor, care este traversată în următoarea ordine: Elemente ,
Ferestre , Application.Resources , Sistem, interfața cu utilizatorul (UI) elemente pot lega la
resursele aplicatie domeniul de aplicare. În plus, în cazul în care schimbarea de resurse, sistemul
de resurse asigură că proprietățile elementelor care sunt legate de aceste resurse sunt actuali zate
automat pentru a reflecta schimbarea. Iar clasa de bază după care aceste schimbări vor avea loc
va fi “ViewModel ”.
<Application.Resources >
<locator:ViewModel x:Key="ViewModel"/>
</Application.Resources >
Capitolul 3. Dezvoltarea proiectului din punct de vedere al utilizatorului
3.1 Pagina principal ă
Pagina principală constă în figura de mai jos ( Fig. 3.1 – Button Page ) unde am creat un User
Control.
Fig. 3.1 – Button Page
Acesta va fi legat prin Data Binding la “ViewModel ”.
d:DesignHeight ="640" d:DesignWidth ="800" DataContext ="{Binding Source={StaticResource
ViewModel }, Path=HomePageViewModel}"
Pentru a ceastă pagină am creat un grig și 5 butoane, Option, New Game, Help, License și Exit.
Iar la fiecare buton în parte am atribuit culoarea de mai sus și de asemenea o metodă.
Vom avea un obiect “oldContent” căruia îi vom da valuarea Content.
public partial class HomePage : UserControl
{
object oldContent;
public HomePage()
{
InitializeComponent();
oldContent = Content;
}
Vom crea o metodă pentru butonul NewGame, unde vom crea o nouă pagină de jocuri :
private void NewGame( object sender, RoutedEventArgs e)
{
var newPlayerPage = new PlayerPage ();
Content = newPlayerPage;
}
Vom salva nivelul ales din pagina de opțiune:
private void Option(object sender, RoutedEventArgs e)
{
var optionsPage = new OptionsPage ();
optionsPage.SaveOptions += optionsPage_SaveOptions;
Content = optionsPage;
}
După salvarea nivelului afișăm un mesaj cu opțiunea salvată, după care prin obiectul ”Content ”
revenim la pagina principl ă.
void optionsPage_SaveOptions( object sender)
{
MessageBox .Show(string.Format( "Save options, level chosen {0}" ,
Properties. Settings .Default.Level));
Content = oldContent;
}
Pentru butonul Help și License vom crea pagini noi schimbând și obiectul Content.
private void HelpPage( object sender, RoutedEventArgs e)
{
var helpPage = new HelpPage ();
Content = helpPage;
}
private void LicensePage( object sender, RoutedEventArg s e)
{
var licensePage = new LicensePage ();
Content = licensePage;
}
Iar în cele din urmă, prin butonul Exit vom putea închide aplicația. Codul de mai jos face acest
lucru automat.
private void Exit(object sender, RoutedEventArgs e)
{
Application .Current.Shutdown();
}
În acest User Contrl vom avea un Grid, împarțit în 7 linii și 7 coloane. Iar o celulă este locul unde
se pot plasa piesele. În acest caz le -am numit place s, mai s us.
3.2 Pagina de nivel
Fig. 3.2 – Level Page
Vom avea următoarea enumerare “ SelectState ” în care (Fig. 3.2 – Level Page) :
Neutral, va fi folosit la început
PlaceNew, va fi folosit pentru plasarea pieselor
RemoveOpponentPieces, pentru a lua o piesă de la adversar
MoveExisting, pentru a muta o piesă
public partial class Level : UserControl
{
public Level()
{
InitializeComponent();
}
static Games game = ViewModel .GameViewModel.Game;
static PlayersViewModel players = ViewModel .PlayerViewModel;
enum SelectState
{
NeutralPlace,
the_New_Place,
PiecesRemove,
MoveExistingPieces
}
Aici vom folosi Neutral din enumerare, p entru al atribui variabilei “currentState”.
private static SelectState current_State = SelectState .Neutral Place;
Varibila “place” este egal cu “ game.Places.FirstOrDefault ” care returnează primul element din
secvența de mai jos, iar dacă nu s -a gasit acest element default.
private void Ellipse_MouseDown( object sender, MouseButtonEventArgs e)
{
var TheEllipse = sender as Ellipse;
if (TheEllip se == null) return;
var the_place = game.Places.FirstOrDefault(t => TheEllipse.Tag as
string == t.PlaceName);
if (the_place == null) return;
Vom verifica dacă starea jocului îi să se ia o piesă de la celălalt jucător. După care vom mai
verifica dacă jucătorul care urmează este numărul 1 și dacă starea de loc al jucătorului este P2.
Adică dacă jucătorul are piese.
În acest caz se va lua o piesă de la jucătorul 2.
Dacă jucatorul al d oilea are 3 piese pe tablă, dar nu mai are piese pe care sa le poată pune pe
tablă, jocul va continua.
Dar dacă jucatorul al doilea are doar 2 piese pe tablă, dar nu mai are piese pe care sa le poată
pune pe tablă, jocul va lua sfârșit. Și o nouă pagină va aparea: “ GameOverPage ”.
if (current_State == SelectState .PiecesRemove)
{
if (players.Player1.IsPlayersTurn && the_place.Status ==
PlaceStatus .P2)
{
the_place.Status = PlaceStatus .Unoccupied;
players.Player2.PiecesLeft –;
if (players.Player2.PiecesLeft == 3 && players.Player2.InvisiblePieces ==
0)
game.State = GameState .Flying;
if (players.Player2.PiecesLeft == 2 && players.Player2.InvisiblePieces ==
0)
{
// game over logic
players.Player1.IsPlayersTurn = false;
players.Player2.IsPlayersTurn = false;
game.Winner = players.Player1.Name;
game.State = GameState .Over;
this.Content = new GameOverPage ();
}
current_State = SelectState .NeutralPlace;
players.SwitchTurns();
}
Acela și lucru va trebui făcut și pentru al doilea jucător.
După care vom mai verifica dacă jucătorul care urmează este numărul 1 și dacă star ea de loc al
jucătorului este P1 . Adică dacă jucătorul are piese.
În acest caz se va lua o piesă de la jucătorul 1. Dacă primul jucator are 3 piese pe tablă, dar nu
mai are piese pe care sa le poată pune pe tablă, jocul va continua.
Dar dacă primul jucatorul are doar 2 piese pe tablă, dar nu mai are piese pe care sa le poată pune
pe tablă, jocul va lua sfârșit. Și o nouă pagină va aparea: “ GameOverPage ”.
else if (players.Player2.IsPlayersTurn && the_place.Status == PlaceStatus .P1)
{
the_place.Status = PlaceStatus .Unoccupied;
players.Player1.PiecesLeft –;
if (players.Player1.PiecesLeft == 3 && players.Player1.InvisiblePieces
== 0)
game.State = GameState .Flying;
if (players.Player1.PiecesLeft == 2 && player s.Player1.InvisiblePieces
== 0)
{
// game over logic
players.Player1.IsPlayersTurn = false;
players.Player2.IsPlayersTurn = false;
game.Winner = players.Player2.Name;
game.State = GameState .Over;
this.Content = new GameOverPage ();
}
current_State = SelectState .NeutralPlace;
players.SwitchTurns();
}
Se va schimba rândul jucătorului apelând metoda SwitchTurns.
Dacă jocul poate continua, și dacă locul este diferit de neocupat, înseamnă că pe acel loc este deja
o piesă, iar în acest caz nu vom face nimic, ci vom trece la următoarea etapă, în care dacă este
rândul primul jucător, se va pune status -ul corespunzător.
Se va scădea de fiecare dată pisele invizibi le, iar numărul pieselor rămase va crește. Cu alte
cuvinte jucătorul va pune o piesă pe tablă și va veni rândul celuilalt să mute.
else if (game.State == GameState .Placing)
{
if (the_place.Status != PlaceStatus .Unoccupied)
{
// can't place on top of another piece
}
else if (players.Player1.IsPlayersTurn)
{
the_place.Status = PlaceStatus .P1;
players.Player1. InvisiblePieces –;
players.Player1.PiecesLeft++;
if (players.Player1.AddNewMills(game.Places,
PlaceStatus .P1, the_place))
{
current_State = SelectState .PiecesRemove;
}
else players.SwitchTurns();
}
Același lucru se va face și pentru al doilea jucâtor.
Dacă jocul poate continua, și dacă locul este diferit de neocupat, înseamnă că pe acel loc este deja
o piesă, ia r în acest caz nu vom face nimic, ci vom trece la următoarea etapă, în care dacă este
rândul primul jucător, se va pune status -ul corespunzător.
Se va scădea de fiecare dată pisele invizibile, iar numărul pieselor rămase va crește. Cu alte
cuvinte jucătoru l va pune o piesă pe tablă și va veni rândul celuilalt să mute.
else if (players.Player2.IsPlayersTurn)
{
the_place.Status = PlaceStatus .P2;
players.Player2.InvisiblePieces –;
players.Player2.PiecesLeft++;
if (players.Player2.AddNewMills(game.Places,
PlaceStatus .P2, the_place))
{
current_State = SelectState .PiecesRemove;
}
else players.SwitchTurns();
}
În caz ca toate piesele sau pus deja pe tablă jocul poate continua .
if (players.Player1.InvisiblePieces == 0 &&
players.Player2.InvisiblePieces == 0)
{
game.State = GameState .Moving;
}
În cazul în care nu s -a selectat nici o piesă ca să se poată muta, se va putea scoate în evidență
piesa, prin îngoșarea marginii piesei respective.
else if (game.State == GameState .Moving)
{
if (game.CurrentlyMovingPiece == null)
{
if (players.Player1 .IsPlayersTurn && place.Status ==
PlaceStatus .P1 ||
players.Player 2.IsPlayersTurn && place.Status ==
PlaceStatus .P2)
{
game.CurrentlyMovingPiece = place;
place.Highlight();
}
}
Când s -a selectat piesa, se va putea muta. Acest lucru se va întâmpla în continuare pentru fiecare
dintre jucători, separate.
Se va verifica dacă s -a selectat sau nu o piesă. În continuare, când va fi rândul primului jucător și
s-a ales o piesă pentru mutare se v -a putea muta piesa
else if (game.State == GameState .Flying)
{
if (game.CurrentlyMovingPiece == null)
{
if (players.Player1.IsPlayersTurn && the_place.Status == PlaceStatus .P1 ||
players.Player2.IsPlayersTurn && the_place.Status == PlaceStatus .P2)
{
game.CurrentlyMovingPiece = the_ place;
the_place.Highlight();
}
}
else
{
if (the_place.Status == PlaceStatus .Unoccupied)
{
the_place.Status = game.CurrentlyMovingPiece.Status;
game.CurrentlyMovingPiece.Status = PlaceStatus .Unoccupied;
if (players.Player1.IsPlayersTurn)
{
if (players.Player1.AddNewMills(game.Places, PlaceStatus .P1, the_place))
{
current_State = SelectState .PiecesRemove;
}
else players.SwitchTurns();
}
else if (players.Player2.IsPlayersTurn)
{
if (players.Player2.AddNewMills(game.Places, PlaceStatus .P2, the_place))
{
current_State = SelectState .PiecesRemove;
}
else players.SwitchTurns();
}
}
game.CurrentlyMovingPiece.UnHighlight();
game.CurrentlyMovingPiece = null;
}
}
}
private void MouseUpOnElipse( object sender, MouseButtonEventArgs e)
{
var ellipse = sender as Ellipse;
if (ellipse == null) return;
var tile = game.Places.FirstOrDefault(t => ellipse.Tag as string ==
t.PlaceName);
if (tile == null) return;
}
3.3 Pagina de opțiuni
În pagina de opțiuni vom avea 3 butoane de tip radio. Prin cele 3 cazuri din switch, se poate alege
nivelul dorit. Cazul 1 corespunde cu nivelul 1, cazul 2 cu nivelul 2, iar ultimul cu nivelul 3.
Și nu în ultimul rând se va salva opțiunea în variabila “Level”.
public partial class OptionsPage : UserControl
{
public delegate void SaveOptionsEventHandler (object sender);
public event SaveOptionsEventHandler SaveOptions;
public OptionsPage()
{
InitializeComponent();
switch (Properties. Settings .Default.Level)
{
case 1:
level1Radio.IsChecked = true;
break;
case 2:
level2Radio.IsChecked = true;
break;
case 3:
level3Radio.IsChecked = true;
break;
default:
break;
}
}
private void RadioButton_Checked_Level_1( object sender, RoutedEventArgs e)
{
Properties. Settings .Default.Level = 1;
}
private void RadioButton_Checked_Level_2( object sender, RoutedEventArgs e)
{
Properties. Settings .Default.Level = 2;
}
private void RadioButton_Checked_Level_3( object sender, RoutedEventArgs e)
{
Properties. Settings .Default.Level = 3;
}
private void RadioButton_Checked_Level_4( object sender, RoutedEventArgs e)
{
Properties. Settings .Default.Level = 4;
}
private void Button_Click( object sender, RoutedEventArgs e)
{
SaveOptions( this);
}
}
public class SaveOptionsEventArgs : EventArgs
{
public int Level;
public SaveOptionsEventArgs( int level)
{
this.Level = level;
}
}}
Pagina de opțiune de asemenea va avea un buton de salvare, după cum am spus mai sus.
Fugura de mai jos arată această pagină (Fig. 3.3 – Option Page):
3.4 Pagina de joc
Pagina ” The Games Page ” va ar ăta în felul următor (Fig. 3.4 – The Games Page)
Fig. 3.4 – The Games Page
În această pagină vom crea liniile roșii care reprezintă legăturile dintre tiles ( sau places , locurile
unde se pot plasa piesele ).
Fig. 3.3 – Option Page
De fapt liniile sunt 5 pătrate.
Primul îi cel de dinafară, al doilea cel din mijloc, iar în cele din urmă 3 în mijloc.
Pentru primul nivel vom ascunde pătratele 3, 4 și 5 deci numai cele din mijloc.
Pentru cazul al doilea, pentru nivelul 2, nu vom ascunde nici un pătrat, deoarece așa va arăta
nivelul.
Iar pentru ultimul caz, nivelul al treilea, vom ascunde din nou pătratele 3, 4 și 5.
După care vom face ca modificările să fie vizibile și la nivel de window.
public partial class GamePage : UserControl
{
static Games game = ViewModel .GameViewModel.Game;
public GamePage()
{
InitializeComponent();
switch (Properties. Settings .Default.Level)
{
case 1:
patrat3.Visibility = System.Windows. Visibility .Hidden;
patrat4.Visibility = System.Windows. Visibility .Hidden;
patrat5.Visibility = System.Windows. Visibility .Hidden;
break;
case 2:
break;
case 3:
patrat3.Visibility = System.Windows. Visibility .Hidden;
patrat4.Visibility = System.Windows. Visibility .Hidden;
patrat5.Visibility = System.Windows. Visibility .Hidden;
break;
default:
break;
}
game.GameOver += game_GameOver;
}
void game_GameOver( object sender, EventArgs e)
{
patrat1.Visibility = System.Windows. Visibility .Hidden;
patrat2.Visibility = System.Windows. Visibility .Hidden;
patrat3.Visibility = System.Windows. Visibility .Hidden;
patrat4.Visibility = System.Windows. Visibility .Hidden;
patrat5.Visibility = System.Windows. Visibility.Hidden;
}
}}
3.5 Pagina de nume
Numele ”Player1”, “Player2”vor fi de tip TextBlock și de asemenea numerele scrise de desuptul
”Pieces Left” deoarece acestea se vor modifica prin data binding.(Fig 3.5 – Player Name)
Fig. 3.5 – Player Name
Se va face la ”layer1 ” binding la ”PlayerOne.Name ”, adică se face update automat la numele
introdus. La numărul de sub ”Pieces Left” se va face binding la "{Binding Player 1.PiecesLeft}" .
Desigur toate astea se vor fce în interiorul unui Stack Panel.
La se va proceda și pentru cel deal doilea jucător.
<TextBlock Grid.Column ="0" Grid.Row ="0" Text="{Binding Player1.Name}"
Style="{StaticResource NameTextBlockStyle }"
Foreground ="{Binding PlayerOne .Background}" />
<TextBlock Grid.Column ="0" Grid.Row ="1" Text="{Binding
Player1.StatusMessage}" Style="{StaticResource StatusMessageTextBlockStyle }"/>
<StackPanel Grid.Row ="1" Grid.Column ="0" Orientation ="Vertical"
Visibility ="{Binding Player1.PiecesLeftVisibility}" HorizontalAlignment ="Left"
Margin="5,0,0,0">
<TextBlock Text="Pieces Left" FontSize ="12" Foreground ="White"/>
<TextBlock Text="{Binding Player1.PiecesLeft}"
HorizontalAlignment ="Center" FontSize ="12" Foreground ="White"/>
</StackPanel >
<TextBlock Grid.Column ="1" Grid.Row ="0" Text="{Binding Player2.Name}"
Style="{StaticResource NameTextBlockStyle }"
Foreground ="{Binding Player2.Background}" />
<TextBlock Grid.Column ="1" Grid.Row ="1" Text="{Binding
Player2.StatusMessage}" Style="{StaticResource StatusMessageTextBlockStyle }"/>
<StackPanel Grid.Row ="1" Grid.Column ="1" Orientation ="Vertical"
Visibility ="{Binding Player2.PiecesLeftVisibility}" HorizontalAlignment ="Right"
Margin="0,0,20,0" >
<TextBlock Text="Pieces Left" FontSize ="12" Foreground ="White"/>
<TextBlock Text="{Binding Player2.PiecesLeft}"
HorizontalAlignment ="Center" FontSize ="12" Foreground ="White"/>
</StackPanel >
3.6 Pagina de jucături
În acest User Control vom folosi un buton cu textul ”Next” Două textboxuri care pot fi
completate cu numele jucătorilor și un text block cu textul ”VERSUS”. ( Fig. 3.6 – Player Page )
Fig. 3.6 – Player Page
3.7 Pagina de câștigător
În continuare, în următorul User Control vom vom afișa caștigătorul care câștigă. Acest lucru se
va face tot prin binding “Game .Winner ”.
<StackPanel Orientation ="Horizontal">
<TextBlock Text="{Binding Game.Winner}" Style="{StaticResource
TextStyle }"/>
<TextBlock Text=" wins!" Style="{StaticResource TextStyle }"/>
</StackPanel >
Un exemplu va fi figura următoare:
Fig.3.7 – The Winner Page
3.8 Pagina de ajutor
Help page ca fi la îndemâna tuturor. În această pagină s -a utilizat Rich Text Box, pentru a putea fi
folosite și fonturile, dar și pentru a putea modifica mărimea scrisului. Acesta a fost introdus într –
un Scroll Viewer .
Se va folosi metoda ”SetRtf” pentru a localiza fișierul în care se află HelpPage.rt f.
Această metodă va deschide fisierul de care avem nevoie, va citit din el, după care -l va afișa .
public HelpPage()
{
InitializeComponent();
SetRtf("C:\\Users\\Horvath\\Desktop\\Moara –
Licenta\\Resurse\\HelpPage.rtf" );
}
public void SetRtf(string file)
{
using (FileStream fs = File.OpenRead(file))
{
fs.Position = 0;
rtbHelp.SelectAll();
rtbHelp.Selection.Load(fs, DataFormats .Rtf);
}
}
Și aici se va folosi aceeași metoda ca mai sus .
În această pagină s -a utilizat Rich Text Box, pentru a putea fi folosite și fonturile, dar și pentru a
putea modifica mărimea scrisului. Acesta a fost introdus într -un Scroll Viewer.( Fig. 3.9 – License
Page).
Se va folosi metoda ”SetRtf” pentru a localiza fișierul în care se află HelpPage.rtf.
Această metodă va deschide fisierul de care avem nevoie, va citit din el, după care -l va afișa .
public LicensePage()
{
InitializeComponent();
ViewRtf( "C:\\Users\\Horvath\\Desktop\\Moara –
Licenta\\Resurse\\LicensePage.rtf" );
}
public void ViewRtf( string file)
{
using (FileStream files = File.OpenRead(file))
{
files.Position = 0;
rtbLicense.SelectAll();
rtbLicense.Selection.Load(files, DataFormats .Rtf);
}}
Capitolul 4. Utilizarea aplicației.
Acestă apliacație este un joc de strategie. Pe prima pagină se pot găsi 5 butoane.
La pagina de opțiuni exită 3 nivele din care se pot alege, cu câte o imagine corespunzătoare. În
clipa în care se salvează opțiunea, va apărea o fereastră cu va informa și va duce înapoi jucătorul
la pagina principal.
Jocul în sine, constă în a pune una lângă alta sau în aceeși linie 3 piese pentru a face moara.
Liniile roșii de pe tablă vor arăta legaturile. În funcție de nivelul ales numărul pieselor diferă.
Fiecare jucător are la dispoziție: pentru primul nivel 5 pi ese, pentru al doilea 7, iar pentru ultimul
9 piese. Când toate piesele au fost puse în joc, pisele se vor putea muta de pe o poziție pe alta.
Scopul va fi același, de a face moara. Când se face moara, jucătorul care a r ealizat acest lucru, va
putea să ia o piesă de la adversar. Jocul se termină când unul dintre cei doi jucători va rămâne
numai cu două piese, caz în care celălalt câștigă, deoarece avem nevoie de trei piese pentru a face
moara.
Concluzii
Model -View -View Model (MVVM) este un model de proiectare pentru construirea de user
interface . Acesta descrie modul în care se poate diviza în trei părț i: model unde datele aplicației
unt stocate. Datele respective sunt independente de orice UI. .
Un view model: este o reprezentare cod a datelor și a operațiunilor pe un UI . Se poate spune că
este legătura dintre model și view.
Un view es te un user interface interactiv , vizibil, reprezentând starea view modelului. Se afișează
informații de la view model, trimițând comenzi pentru view model (de exemplu, a tunci când
utilizatorul face cl ic pe butoane), și actualizează de fiecare dată când view model -ul este
modificat .
Acest proiect descrie o aplicație implementată și bazată pe modelul MVVM.
Bibliografie
1. Laslo E ., Ionescu V. S., Algoritmică C++, 2011 Matrix rom București
2. https://msdn.microsoft.com/library/windows/apps/xaml/Hh758320#change_notification
3. https://msdn.microsoft.com/en -us/library/edzehd2t(v=vs.110).aspx
4. http://ww w.c-sharpcorner.com/UploadFile/dacca2/implement -ienumerable -interface -in-
C-Sharp/
5. http://jen20.com
6. http://leecampbell.blogspot.ro/2010/08 /reactive -extensions -for-net.html
7. http://blog.scottlogic.com/2012/04/20/everything -you-wanted -to-know -about –
databin ding-in-wpf-silverlight -and-wp7-part-two.html
8. http://www.codeproject.com/Articles/646361/Reactive -Programming -for-NET -and-
Csharp -Developers
9. https://msdn.microsoft.com/en -us/library/gg405484%28v=pandp.40%29.aspx
10. https://reactiveproperty.codeplex.com/
11. http://www.mastersgames.com/rules/morris -rules.htm
12. https://www.nuget.org/
13. http://www.codeproject.com/Articles/328361/Understanding -and-Implementing –
Observer -Pattern -in
14. http://www.introtorx.com/
15. http://www.danharman.net/2011/06/13/using -reactiveui -to-integrate –
inotifypropertychanged -iobservable/
16. https://msdn.microsoft.com/library/windows/apps/xaml/Hh758320#change_notification
17. http://www.nudoq.org/#!/Packages/RD1BACommon/ReactiveUI.Xaml/IReactiveComma
nd
18. http://blogs.msdn.com/b/mikehillberg/archive/2009/03/20/icommand -is-like-a-chocolate –
cake.aspx
19. http://reactiveui.readthedocs.org/en/latest/
20. http://blog.scottlogic.com/2012/04/20/everything -you-wanted -to-know-about –
databinding -in-wpf-silverlight -and-wp7-part-two.html
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Asist. univ. Laslo Eugen [622047] (ID: 622047)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
