Quelques raccourcis :
Ceci est un petit site de support pour le cours 420-202-RE – Structures de données et programmation orientée objet.
Vous trouverez aussi des liens sur divers langages (dont C#, notre outil de prédilection dans ce cours) un peu partout dans http://h-deb.ca/
Les diverses sections de cette page (en fonction desquelles vous trouverez quelques liens dans l'encadré à droite) vous mèneront elles-aussi sur des pistes qui vous permettront d'explorer un peu plus par vous-mêmes, de valider vos acquis et d'enrichir votre apprentissage
![]() |
Cliquez sur cette cible pour le plan de cours, sous forme électronique |
![]() |
Je corrige les programmes en appliquant des codes de correction. Vous trouverez ici la liste des codes les plus fréquents. |
![]() |
Ma stratégie de correction en tant que telle (pour le code, à tout le moins) est résumée ici. |
![]() |
Cliquez sur cette cible pour un résumé des principales règles de programmatique en vigueur dans ce cours. |
![]() |
Cliquez sur cette cible pour les normes appliquées dans ce cours, en ce qui a trait au pseudocode |
Date | Séance | Détails | ||||
---|---|---|---|---|---|---|
24 janvier |
Au menu :
Notez qu'en ce premier cours, nous investissons nos efforts sur deux aspects, soit :
Ça fait un gros cours pour démarrer la session, mais nous réinvestirons le tout cours après cours, alors ça va entrer doucement dans notre tête, et s'intégrer à notre praxis émergente 🙂.
À titre de « petit bonbon », nous avons vu en début de séance que les fonctions qui se limitent à un seul return peuvent être écrites de manière simplifiée, et nous avons appris que votre chic prof, souhaitant vous encourager à écrire de courtes fonctions qui font une et une seule chose (et le font bien!), acceptera cette syntaxe si elle est bien utilisée. Ainsi, ceci :
... peut s'écrire de manière équivalente sous la forme suivante :
... alors que ceci :
... pourra s'écrire comme suit
D'autres simplifications d'écriture viendront plus tard dans la session. Nous avons enfin défini (et illustré par des exemples) un certain nombre de termes : classe, instance, attribut d'instance, propriété d'instance (avec volets get, set – nous ajouterons sous peu le mot init), constructeur par défaut, constructeur paramétrique, qualifications d'accès private et public (il y en a d'autres), encapsulation, membre d'instance (non-static; nous ajouterons bientôt membre de classe : static), invariant, précondition et postcondition... Ouf! À propos des termes utilisés, les notes informelles prises en classe étaient comme suit (je vous mets ça « brut ») :
|
|||||
26 janvier |
Au menu :
À titre de simplification (et de bonbon de fin de séance), j'ai aussi montré :
|
|||||
31 janvier |
Au menu :
Quelques trucs pour vous aider... Si vous cherchez un exemple simpliste de programme de test interactif, vous pouvez utiliser celui-ci (vous pouvez aussi vous en écrire un plus à votre goût; je ne ramasserai que la classe Crypteur après tout) :
... notez que je suppose que vous pouvez écrire la fonction Poursuivre puisque nous l'avons fait à quelques reprises cet automne. Si vous souhaitez transformer une string en char[], examinez les méthodes d'instance de cette string (portez attention en particulier à celles dont le nom commence par To...). Si vous souhaitez transformer une string en majuscules, examinez les méthodes d'instance de cette string (portez attention en particulier à celles dont le nom commence par To...). Si vous souhaitez transformer un char[] en string, sachez que string expose un constructeur paramétrique acceptant un char[] en paramètre. Par exemple, comparez les deux exemples ci-dessous (l'un des deux est plus pertinent que l'autre pour vos fins) :
Si vous souhaitez modifier un char, alors... que diriez-vous de la simple arithmétique? Par exemple :
|
|||||
2 février |
Au menu :
S'il reste du temps :
Le code proposé aujourd'hui était :
|
|||||
7 février |
Au menu :
Vous pouvez aussi essayer le programme de test suivant (ajouter le using requis pour que votre Crypteur soit accessible) :
Pour le reste : des exercices, des exercices, et des exercices! Exercice 1a Soit une classe représentant un point de l'espace 2D. Chaque instance de cette classe :
Travail à réaliser :
Exercice 1b Soit une classe représentant un point du quadrant 1 de l'espace 2D. Chaque instance de cette classe :
En cas d'erreur du programme client dans l'utilisation d'une instance de cette classe, la classe doit lever une exception :
Travail à réaliser :
Exercice 2 Soit une classe représentant un compte bancaire (très simple). Les instances de cette classe devront offrir les services suivants :
Vous devez faire en sorte que par défaut, ce compte bancaire soit créé avec un solde à zéro; si le constructeur paramétrique est utilisé, la valeur précisée lors du processus d'instanciation doit être positive ou égale à 0. Les méthodes permettant d'effectuer les dépôts et les retraits recevront en paramètre le montant à déposer ou à retirer selon le cas, qui doit être un entier positif strictement plus grand que 0, et ne retourneront rien. Ce type de compte bancaire a un invariant : il ne pourra à aucun moment avoir un solde négatif. En vertu de l'encapsulation, vous devez vous assurer du respect de cet invariant, donc faire en sorte qu'en tout temps votre objet reste valide. Dans le cas où le code client tenterait de créer une instance en y attribuant un solde négatif, votre classe devra lever une exception de type SoldeInvalideException. Dans le cas où le code client tenterait de retirer un montant supérieur au solde du compte, ou encore de déposer un montant négatif ou nul, votre objet devra évidemment refuser l'opération et lever une exception de type OpérationInvalideException. Travail à réaliser :
Une solution possible serait ceci : ClasseCompteBancaire.html Exercice 3 Plus difficile, car moins directif. Vous développez un jeu impliquant des héros et des monstres. Les règles sont les suivantes :
Travail à réaliser :
Exercice 4 Sachant que la couleur de l'affichage de texte dans un écran console est donnée par la propriété Console.ForegroundColor, et sachant qu'il est possible de positionner le curseur où l'affichage de texte se fera à l'aide de la méthode Console.SetCursorPosition, écrivez une classe Carré telle que :
Travail à réaliser :
|
|||||
9 février |
Je serai absent ce matin dû à une rencontre du WG21 à Issaquah. Vous pourrez suivre mes aventures sur ../../Sujets/Orthogonal/wg21-2023-Issaquah.html |
|||||
14 février |
Au menu :
|
|||||
16 février |
Au menu :
Le code auquel nous sommes arrivé(e)s pour l'exercice 3 de la séance S04 est disponible ici Le code auquel nous sommes arrivé(e)s pour l'exercice 4 de la séance S04 est disponible ici Pour les éléments de vocabulaire quant aux relations, informellement, ce que nous avons relevé est :
Pour vous faire pratiquer...
Répondez aux questions suivantes :
À titre de solutionnaire pour cet exercice :
À titre de rappel, une méthode d'instance est une fonction membre non-static
À titre de rappel, une méthode de classe est une fonction membre static
À titre de rappel, une propriété d'instance est une pseudo-variable membre non-static offrant un contrôle d'accès en lecture (get) ou en écriture (set, init)... ou les deux
À titre de rappel, un attribut d'instance est variable membre non-static
|
|||||
21 février |
Au menu :
|
|||||
23 février |
Votre chic prof a dû s'absenter pour des raisons familiales. Toutefois, pour ne pas que vous ne vous ennuyiez trop de lui, il vous laisse ce petit cadeau que vous voudrez peut-être utiliser dans vos tests de la classe Histogramme que vous concevrez dans le cadre de votre labo 01 :
Amusez-vous bien! |
|||||
28 février |
Jour de mise à niveau (cours suspendus) |
|||||
2 mars |
Jour de mise à niveau (cours suspendus) |
|||||
7 mars |
Au menu :
Petit exemple simple avec une List<int> :
Le chemin que nous avons suivi fut « classique » :
Ce serait cool de pouvoir écrire une fonction Dessiner(Forme f) qui (a) fait une copie de sauvegarde de la couleur du texte à l'écran, (b) la remplace par la couleur de la Forme, (c) dessine la Forme, puis (d) remet la couleur originale pour le texte à l'écran... sauf que f.Dessiner n'existe pas. Pour résoudre ce problème, nous avons d'abord inséré une méthode Dessiner non virtuelle dans Forme, pour se rendre compte que ça ne fait rien de pertinent. Nous avons ensuite virtual (dans la classe Forme) et override (dans les classes Triangle et Rectangle), suite à quoi l'affichage s'est mis à dessiner des trucs colorés à l'écran, pour notre plus grand bonheur! En bout de ligne, on arrive à quelque chose comme ce qui suit. J'ai ajouté un peu de validation (note : c'est une première étape; nous raffinerons le modèle sous peu) :
Résumé des nouvelles idées de cette partie du cours : Héritage d'implémentation (enfant est un cas particulier du parent). Bon, c'est pas nouveau d'aujourd'hui, mais... Polymorphisme :
|
|||||
9 mars |
Au menu :
N'oubliez pas de remettre le labo 01 (au plus tard à 7 h 59 sinon la boîte de remise sera close!) |
|||||
14 mars |
Au menu :
|
|||||
16 mars |
Au menu :
|
|||||
21 mars |
Au menu :
|
|||||
23 mars |
Au menu :
Si vous le souhaitez, un exercice :
+---+---+---+---+ | 1 | 3 | +-\ ----+-\ ----+ # x x x x # +----\ -+----\ -+ | 2 | 4 | +---+---+---+---+
Remise du labo 02 |
|||||
28 mars |
Au menu :
|
|||||
30 mars |
Au menu :
Le code de la classe Liste conçue en classe était :
Le code de la classe File construite en classe sur les fondations du substrat Liste était :
Enfin, nous avons fait un premier pas (rapide, humble, mais amusant je l'espère) dans le monde de la programmation générique en transformant notre Tableau de la séance S14 en Tableau<T> :
En espérant que le tout vous ait diverti! |
|||||
4 avril |
Au menu :
Prudence : mardi selon l'horaire du jeudi |
|||||
6 avril |
s/o |
Journée pédagogique, cours suspendus |
||||
11 avril |
Au menu :
Exercice 0 Écrivez une classe FilePrioritaire<T> ayant les caractéristiques suivantes :
Code de test possible :
Affichage attendu :
Pour le plaisir : pouvez-vous faire une FilePrioritaire<Client> pour une classe Client où chaque client a un nom, un prénom et une dette, et où les clients les plus endettés sont prioritaires sur les moins endettés? Si deux clients sont aussi endettés l'un que l'autre, alors la priorité doit respecter l'ordre lexicographique (ordre croissant de nom, puis de prénom si deux noms sont équivalents). Solution possible :
Question : est-ce que le substrat est bien choisi? Expliquez votre réponse. Exercice 01 Sachant que nous avons implémenté les algorithmes TrouverSi et CompterSi. Ajoutons quelques algorithmes génériques à notre banque d'outils : EX01.0 – Écrivez l'algorithme Permuter<T>(ref T a, ref T b) qui permutera les valeurs de a et de b, de telle sorte que l'exécution du programme suivant :
... affiche ce qui suit :
EX01.1 – Écrivez l'algorithme RotaterGauche<T>(List<T> src) qui retourne une List<T> contenant un équivalent des éléments de la List<T> reçue en paramètre, mais où les éléments de src ont été décalés à gauche d'une position de manière cyclique (l'élément à la position 0 dans src est placée à la position Count-1 dans la List<T> résultante), de telle sorte que le programme suivant :
... affiche ce qui suit (notez la première ligne qui est vide, car nous avons opéré sur... une séquence vide!) :
Note : essayez d'implémenter cet algorithme sans allouer de mémoire (sans utiliser new). EX01.2 – Écrivez l'algorithme RotaterDroite<T>(List<T> src) qui retourne une List<T> contenant un équivalent des éléments de la List<T> reçue en paramètre, mais où les éléments de src ont été décalés à droite d'une position de manière cyclique (l'élément à la position Count-1 dans src est placée à la position 0 dans la List<T> résultante), de telle sorte que le programme suivant :
... affiche ce qui suit (notez la première ligne qui est vide, car nous avons opéré sur... une séquence vide!) :
Note : essayez d'implémenter cet algorithme sans allouer de mémoire (sans utiliser new). EX01.3 – Écrivez la fonction Concaténer<T>(List<T> lst0, List<T> lst1) qui retournera une List<T> contenant les mêmes éléments que lst0, dans l'ordre, suivis des éléments de lst1, dans l'ordre. EX01.4 – Écrivez l'algorithme Transformer<T,U>(List<T> src, Func<T,U> fct) qui retourne une List<U> contenant un équivalent des éléments de la List<T> reçue en paramètre, mais transformés par application de la fonction fct, de telle sorte que le programme suivant :
... affiche ce qui suit :
EX01.5 – Écrivez l'algorithme Cumuler<T,U>(List<T> src, Func<U,T,U> accum, U init) qui reçoit en paramètre une List<T>, une fonction applicable à un U et à un T et retournant un U, de même qu'une valeur initiale de type U, et retourne l'accumulation, de telle sorte que le programme suivant :
... affiche ce qui suit :
EX01.6 – Écrivez la fonction FiltrerSi<T>(List<T> src, Func<T,bool> pred) qui retournera une List<T> contenant les mêmes éléments que src, dans le même ordre, à ceci près que tous les éléments respectant le prédicat pred auront été supprimés. EX01.7 – Écrivez la fonction RemplacerSi<T>(List<T> src, Func<T,bool> pred, T post) qui retournera une List<T> contenant les mêmes éléments que src, dans l'ordre, mais dont chaque élément satisfaisant le prédicat pred aura été remplacé par post. |
|||||
13 avril |
Au menu :
|
|||||
18 avril |
Au menu :
Solution à EX01.0 :
Solution à EX01.1, version qui ne modifie pas src :
Solution à EX01.1, version qui modifie src :
Solution à EX01.2 : je vous laisse la faire en exercice 🙂 Solution (naïve) à EX01.3 :
Solution (moins naïve) à EX01.3 :
Solution (plus chouette et plus générale) à EX01.3 :
Solution à EX01.4 :
Solution à EX01.5 :
Solution à EX01.6 :
Solution (naïve) à EX01.7 :
Solution (moins naïve) à EX01.7 :
Solution (beaucoup moins naïve) à EX01.7 :
Solution pour Trouver :
|
|||||
20 avril |
Au menu : vous êtes en grève aujourd'hui... |
|||||
25 avril |
Au menu :
Le code fait aujourd'hui suit. Pour la classe Liste<T> :
... et pour la classe Tableau<T> :
|
|||||
27 avril |
Au menu :
|
|||||
2 mai |
Au menu :
Présentation informelle de la PFI Pour le plaisir : une petite démo de ce que sera la PFI (vidéo gracieuseté de Maxime Barakatt, fait pour une version antérieure mais valide pour la version de cette année – pour le comportement apparent, le seul changement est que les monstres peuvent désormais manger les personnages! : demo-AS.mp4) |
|||||
4 mai |
s/o |
Journée d'examen de français / formation générale (cours suspendus) |
||||
9 mai |
Au menu :
L'exemple d'entrée-sortie sur des flux textes sans bloc using mais avec bloc finally était :
L'exemple d'entrée-sortie sur des flux textes avec bloc using était :
|
|||||
11 mai |
Au menu :
Les exemples vus en classe sont les suivants. Sélectives – exemple 0
Sélectives – exemple 1
Sélectives – exemple 2
Sélectives – exemple 3
Sélectives – exemple 4
|
|||||
16 mai |
Au menu :
|
|||||
18 mai |
Au menu :
|
|||||
23 mai |
Examen final Dernière journée pour remettre la PFI |
Vous trouverez ici quelques documents, la plupart petits, qui peuvent vous donner un petit coup de pouce occasionnel.
Vous trouverez aussi des exemples de code C# dans la section Divers – C# du site, mais notez que je n'ai pas nécessairement harmonisé ces exemples (écrits pour des cours plus avancés, sous forme de survols) aux standards de programmation appliqués dans le présent cours. À lire avec prudence et discrimination, donc.
Si vous cherchez les énoncés des travaux pratiques, vous pourrez entre autres les trouver ci-dessous (note : ce tableau est celui de H2021; je ne l'ai pas mis à jour pour H2023).
Travail | Nom | Rôle |
---|---|---|
Labo 00 |
Le Cryptographe |
Travail sur l'encapsulation et la conception de classes |
Labo 01 |
À venir |
Travail sur l'héritage et le polymorphisme |
Labo 02 |
À venir |
Travail sur les interfaces, l'encapsulation et le code générique |
Labo 03 |
À venir |
Travail sur les dictionnaires, les fonctions génériques et les entrées / sorties |
Production finale d'intégration |
À venir |
Quelques solutionnaires suivent. En espérant que ça vous aide à organiser vos idées!
L'exercice 1a de la séance S05 porte sur une classe Point générale dont chaque instance représente un point dans n'importe quel quadrant du plan cartésien. Un programme de test pour cet exercice suit.
//---------------------------------------------------------
// Programme de test de la classe Point
//
// Ce programme vérifie que la classe Point fait
// correctement son travail
//
// par Pierre Prud'homme, 2013 (retouché par Patrice Roy, 2020 puis 2023)
//---------------------------------------------------------
using System;
// test du constructeur par défaut
Point pDéfaut = new ();
AfficherPoint("Test du constructeur par défaut", pDéfaut);
AfficherSéparateur();
// test du constructeur paramétrique
Point pQuadrant1 = new (1.1f, 1.1f);
Point pQuadrant2 = new (-2.2f, 2.2f);
Point pQuadrant3 = new (-3.3f, -3.3f);
Point pQuadrant4 = new (4.4f, -4.4f);
AfficherPoint("Test du constructeur paramétrique pour Q1", pQuadrant1);
AfficherSéparateur();
AfficherPoint("Test du constructeur paramétrique pour Q2", pQuadrant2);
AfficherSéparateur();
AfficherPoint("Test du constructeur paramétrique pour Q3", pQuadrant3);
AfficherSéparateur();
AfficherPoint("Test du constructeur paramétrique pour Q4", pQuadrant4);
AfficherSéparateur();
// test du mutateur de x et de y
pQuadrant1.X = 10.10f;
pQuadrant1.Y = 10.10f;
AfficherPoint("Test du mutateur de Q1", pQuadrant1);
AfficherSéparateur();
TesterDistance();
// --------------------------
// fin du programme principal
// --------------------------
static bool AssezProches(float f0, float f1) => Math.Abs(f0 - f1) <= Math.Pow(10, -6);
static void TesterDistanceEx(Point p0, Point p1, float attendu)
{
float dist = p0.Distance(p1);
if (AssezProches(dist, attendu))
{
Console.WriteLine($"Distance entre [{p0.X},{p0.Y}] et [{p1.X},{p1.Y}] environ {attendu} comme prévu");
}
else
{
Console.WriteLine($"ERREUR : distance entre [{p0.X},{p0.Y}] et [{p1.X},{p1.Y}] == {dist}, mais {attendu} est attendu");
}
}
static void TesterDistance()
{
TesterDistanceEx(new Point(0,0), new Point(0,0), 0);
TesterDistanceEx(new Point(0,0), new Point(1,0), 1);
TesterDistanceEx(new Point(0,0), new Point(0,1), 1);
TesterDistanceEx(new Point(0,0), new Point(1,1), (float) Math.Sqrt(2));
}
static void AfficherPoint(string message, Point p)
{
Console.WriteLine($"{message} : le point vaut [{p.X}, {p.Y}]");
}
static string CréerSéparateur(int nbCar) => new string('-', nbCar);
static void AfficherSéparateur()
{
const int NB_CAR_LIGNE = 72;
Console.WriteLine($"\n{CréerSéparateur(NB_CAR_LIGNE)}\n");
}
L'exercice 1b de la séance S04 porte sur une classe PointQuadrant1 générale dont chaque instance représente un point situé dans le premier quadrant du plan cartésien. Un programme de test pour cet exercice suit.
//---------------------------------------------------------
// Programme de test de la classe PointQuadrant1
//
// Ce programme vérifie que la classe Point fait
// correctement son travail
//
// par Pierre Prud'homme, 2013 (retouché par Patrice Roy, 2020 puis 2023)
//---------------------------------------------------------
using System;
TesterConstructeurParamétrique();
TesterMutateurs();
// --------------------------
// fin du programme principal
// --------------------------
static void AfficherPoint(string message, PointQuadrant1 p)
{
Console.WriteLine($"{message} :");
Console.WriteLine($"Le point vaut [{p.X}, {p.Y}]");
}
static string CréerSéparateur(int nbCar) => new string('-', nbCar);
static void AfficherSéparateur()
{
const int NB_CAR_LIGNE = 72;
Console.WriteLine($"\n{CréerSéparateur(NB_CAR_LIGNE)}\n");
}
static void TesterConstructeurParamétrique()
{
// tests du constructeur paramétrique
TesterPoint(1.1f, 1.1f);
TesterPoint(-2.2f, 2.2f);
TesterPoint(-3.3f, -3.3f);
TesterPoint(4.4f, -4.4f);
}
static void TesterPoint(float x, float y)
{
Console.WriteLine($"Point reçu : [{x}, {y}]");
try
{
PointQuadrant1 p = new (x, y);
AfficherPoint("Test réussi du constructeur paramétrique pour PointQuadrant1", p);
}
catch (CoordonnéeXInvalideException)
{
Console.WriteLine("Point invalide en x lors de la construction");
}
catch (CoordonnéeYInvalideException)
{
Console.WriteLine("Point invalide en y lors de la construction");
}
AfficherSéparateur();
}
static void TesterMutateurs()
{
PointQuadrant1 p = new (0, 0);
ModifierPoint(p, 11.0f, p.Y);
ModifierPoint(p, p.X, 11.0f);
ModifierPoint(p, 111.0f, 111.0f);
ModifierPoint(p, -22.2f, p.Y);
ModifierPoint(p, p.X, -22.2f);
ModifierPoint(p, -222.0f, -222.0f);
}
static void ModifierPoint(PointQuadrant1 p, float x, float y)
{
Console.WriteLine($"Modification du point : [{x}, {y}]");
try
{
p.X = x;
p.Y = y;
Console.WriteLine("Test réussi de la mutation du point");
}
catch (CoordonnéeXInvalideException)
{
Console.WriteLine("Point invalide en x lors de la modification");
}
catch (CoordonnéeYInvalideException)
{
Console.WriteLine("Point invalide en y lors de la modification");
}
AfficherPoint("Après mutation ", p);
AfficherSéparateur();
}
Le code vu en classe suit.
Notez que cette classe est en évolution alors que la session progresse, et que nous la séparerons éventuellement en plusieurs classes.
using System;
namespace z
{
class Algos
{
public static bool EstEntreInclusif(int val, int min, int max) =>
min <= val && val <= max;
static char[] voyelles = { 'a', 'e', 'i', 'o', 'u', 'y' };
public static bool EstDans(char c, char [] cars)
{
foreach(char ch in cars)
if(ch == c)
return true;
return false;
}
public static bool EstVoyelle(char c) => EstDans(char.ToLower(c), voyelles);
}
}
using System;
namespace z
{
class ForceInvalideException : Exception { }
class Personnage
{
int vie;
public int Vie
{
get => vie;
private set { vie = value; }
}
public bool EstMort => Vie <= 0;
public bool EstVivant => !EstMort;
string nom;
public string Nom
{
get => nom;
private init { nom = value; }
}
int force;
public int Force
{
get => force;
private init { force = value; }
}
public Personnage(string nom, int vie, int force)
{
Nom = nom;
Vie = vie;
Force = force;
}
}
}
using System;
namespace z
{
class Héros : Personnage
{
public Héros(string nom, int force)
: base(nom, GénérerVieInitiale(), ValiderForce(force))
{
}
static int GénérerVieInitiale()
{
const int VIE_MIN = 50,
VIE_MAX = 100;
return new Random().Next(VIE_MIN, VIE_MAX + 1); // bof
}
const int FORCE_MIN = 10,
FORCE_MAX = 20;
static bool EstForceValide(int force) =>
Algos.EstEntreInclusif(force, FORCE_MIN, FORCE_MAX);
static int ValiderForce(int force) =>
EstForceValide(force) ? force : throw new ForceInvalideException();
}
}
using System;
namespace z
{
class NomInvalideException : Exception { }
class Monstre : Personnage
{
public Monstre(string nom, int vie, int force)
: base(ValiderNom(nom), vie, ValiderForce(force))
{
}
static bool ContientVoyelles(string s)
{
foreach (char c in s)
if (Algos.EstVoyelle(c))
return true;
return false;
}
static bool EstNomValide(string nom) =>
nom != null &&
nom.Length <= 5 &&
!ContientVoyelles(nom);
static string ValiderNom(string nom) =>
EstNomValide(nom) ? nom : throw new NomInvalideException();
const int FORCE_MIN = 15,
FORCE_MAX = 25;
static bool EstForceValide(int force) =>
Algos.EstEntreInclusif(force, FORCE_MIN, FORCE_MAX);
static int ValiderForce(int force) =>
EstForceValide(force) ? force : throw new ForceInvalideException();
}
}
Le code vu en classe suit.
Notez que cette classe est en évolution alors que la session progresse, et que nous la séparerons éventuellement en plusieurs classes. Depuis la version précédente, rien n'a changé.
using System;
namespace z
{
class Algos
{
public static bool EstEntreInclusif(int val, int min, int max) =>
min <= val && val <= max;
static char[] voyelles = { 'a', 'e', 'i', 'o', 'u', 'y' };
public static bool EstDans(char c, char [] cars)
{
foreach(char ch in cars)
if(ch == c)
return true;
return false;
}
public static bool EstVoyelle(char c) => EstDans(char.ToLower(c), voyelles);
}
}
Depuis la version précédente, nous avons ajouté la méthode Frapper. Nous avons choisi d'offrir une fonction générale à deux paramètres qui pourra être appelée par d'autres de manière à préserver le côté privé du mutateur de Vie.
using System;
namespace z
{
class ForceInvalideException : Exception { }
class Personnage
{
int vie;
public int Vie
{
get => vie;
private set { vie = value; }
}
public bool EstMort => Vie <= 0;
public bool EstVivant => !EstMort;
public void Frapper(Personnage autre, int dégâts)
{
autre.Vie -= dégâts;
}
string nom;
public string Nom
{
get => nom;
private init { nom = value; }
}
int force;
public int Force
{
get => force;
private init { force = value; }
}
public Personnage(string nom, int vie, int force)
{
Nom = nom;
Vie = vie;
Force = force;
}
}
}
Depuis la version précédente, nous avons ajouté la méthode Frapper. Nous avons choisi d'offrir une fonction spécialisée à un paramètre qui déléguera le travail plus général vers la méthode à deux paramètres du parent.
using System;
namespace z
{
class Héros : Personnage
{
public Héros(string nom, int force)
: base(nom, GénérerVieInitiale(), ValiderForce(force))
{
}
static int GénérerVieInitiale()
{
const int VIE_MIN = 50,
VIE_MAX = 100;
return new Random().Next(VIE_MIN, VIE_MAX + 1); // bof
}
const int FORCE_MIN = 10,
FORCE_MAX = 20;
static bool EstForceValide(int force) =>
Algos.EstEntreInclusif(force, FORCE_MIN, FORCE_MAX);
static int ValiderForce(int force) =>
EstForceValide(force) ? force : throw new ForceInvalideException();
public void Frapper(Personnage autre)
{
const int PCT_MIN = 50,
PCT_MAX = 100;
int dégâts = Force * new Random().Next(PCT_MIN, PCT_MAX + 1) / 100;
Frapper(autre, dégâts);
}
}
}
Depuis la version précédente, nous avons ajouté la méthode Frapper. Nous avons choisi d'offrir une fonction spécialisée à un paramètre qui déléguera le travail plus général vers la méthode à deux paramètres du parent.
using System;
namespace z
{
class NomInvalideException : Exception { }
class Monstre : Personnage
{
public Monstre(string nom, int vie, int force)
: base(ValiderNom(nom), vie, ValiderForce(force))
{
}
static bool ContientVoyelles(string s)
{
foreach (char c in s)
if (Algos.EstVoyelle(c))
return true;
return false;
}
static bool EstNomValide(string nom) =>
nom != null &&
nom.Length <= 5 &&
!ContientVoyelles(nom);
static string ValiderNom(string nom) =>
EstNomValide(nom) ? nom : throw new NomInvalideException();
const int FORCE_MIN = 15,
FORCE_MAX = 25;
static bool EstForceValide(int force) =>
Algos.EstEntreInclusif(force, FORCE_MIN, FORCE_MAX);
static int ValiderForce(int force) =>
EstForceValide(force) ? force : throw new ForceInvalideException();
public void Frapper(Personnage autre)
{
const int PCT_MIN = 50,
PCT_MAX = 75;
int dégâts = Force * new Random().Next(PCT_MIN, PCT_MAX + 1) / 100;
Frapper(autre, dégâts);
}
}
}
Le code de notre programme de test suit.
using System;
Héros héros = new ("Adam", 18);
Monstre monstre = new ("GRR", 75, 20);
Tour àQui = ChoisirProtagoniste();
while(héros.EstVivant && monstre.EstVivant)
{
Console.WriteLine($"Avant le choc, {héros.Nom} a {héros.Vie} vie");
Console.WriteLine($"Avant le choc, {monstre.Nom} a {monstre.Vie} vie");
if(àQui == Tour.héros)
{
Console.WriteLine($"{héros.Nom} frappe {monstre.Nom}");
héros.Frapper(monstre);
àQui = Tour.monstre;
}
else
{
Console.WriteLine($"{monstre.Nom} frappe {héros.Nom}");
monstre.Frapper(héros);
àQui = Tour.héros;
}
Console.WriteLine($"Après le choc, {héros.Nom} a {héros.Vie} vie");
Console.WriteLine($"Après le choc, {monstre.Nom} a {monstre.Vie} vie");
Console.WriteLine(new string('-', 60));
}
if (héros.EstMort)
Console.WriteLine($"{héros.Nom} est mort... snif!");
if (monstre.EstMort)
Console.WriteLine($"{monstre.Nom} est mort... ouf!");
//////////////////////////
static Tour ChoisirProtagoniste() =>
new Random().Next() % 2 == 0? Tour.héros : Tour.monstre;
enum Tour { héros, monstre }
Le code vu en classe suit.
using System;
namespace S04ex04
{
class Point
{
int x;
int y;
static bool EstXValide(int x) => x >= 0;
static bool EstYValide(int y) => y >= 0;
public int X
{
get => x;
set
{
x = EstXValide(value) ? value : throw new ArgumentException();
}
}
public int Y
{
get => y;
set
{
y = EstYValide(value) ? value : throw new ArgumentException();
}
}
public Point(int x, int y)
{
X = x;
Y = y;
}
public Point() : this(0,0)
{
}
}
}
using System;
namespace S04ex04
{
class Carré
{
public Point Position
{
get; private init;
}
public int Côté
{
get; private init;
}
public ConsoleColor Couleur
{
get; private init;
}
public Carré(Point pos, int côté, ConsoleColor couleur)
{
Position = pos;
Côté = côté;
Couleur = couleur;
}
public void Dessiner()
{
ConsoleColor avant = Console.ForegroundColor;
Console.ForegroundColor = Couleur;
for (int ligne = 0; ligne != Côté; ++ligne)
{
for(int col = 0; col != Côté; ++col)
{
Console.SetCursorPosition(Position.X + col, Position.Y + ligne);
Console.Write('#');
}
}
Console.ForegroundColor = avant;
}
}
}
using System;
Carré[] carrés = new Carré[]
{
new Carré(new Point(2, 5), 5, ConsoleColor.Blue),
new Carré(new Point(15, 8), 3, ConsoleColor.Green),
new Carré(new Point(7, 12), 6, ConsoleColor.Yellow)
};
Random r = new ();
for(int i = 0; i != 10; ++i)
{
Console.Clear();
foreach (Carré c in carrés)
c.Dessiner();
System.Threading.Thread.Sleep(1000);
foreach (Carré c in carrés)
{
try
{
c.Position.X += r.Next(0, 5) - 2;
c.Position.Y += r.Next(0, 5) - 2;
}
catch(ArgumentException)
{
}
}
}
Nous avons fait trois petites structures de données dans ce cours, soit :
Le code de la classe suit :
using System;
using System.Collections.Generic;
using System.Diagnostics.SymbolStore;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace z
{
class Tableau
{
int[] Éléments { get; set; }
public int Count { get; private set; }
public int Capacity { get => Éléments.Length; }
public bool EstVide { get => Count == 0; }
public bool EstPlein { get => Count == Capacity; }
public Tableau()
{
Count = 0;
Éléments = new int[0]; // ou Éléments = null
}
public void Add(int valeur)
{
if (EstPlein)
Croître();
Éléments[Count] = valeur;
++Count;
}
private void Croître()
{
int nouvelleCap = Capacity == 0? 64 : Capacity * 2;
int[] nouvTab = new int[nouvelleCap];
for (int i = 0; i != Count; ++i)
nouvTab[i] = Éléments[i];
Éléments = nouvTab;
}
public int Find(int valeur)
{
for (int i = 0; i != Count; ++i)
if (valeur.Equals(Éléments[i]))
return i;
return -1;
}
public void RemoveAt(int indice)
{
for (; indice < Count - 1; ++indice)
Éléments[indice] = Éléments[indice + 1];
--Count;
}
public void Remove(int valeur)
{
int indice = Find(valeur);
if(indice != -1)
RemoveAt(indice);
}
public int At(int indice)
{
return Éléments[indice];
}
}
}
Le code de la classe suit (je ne répète pas la classe Tableau par souci d'économie) :
class PileVideException : Exception { }
class Pile
{
Tableau Substrat { get; init; }
public Pile()
{
Substrat = new();
}
public bool EstVide { get => Substrat.EstVide; }
public void Push(int valeur) // en français : Empiler
{
Substrat.Add(valeur);
}
public int Peek()
{
if (EstVide)
throw new PileVideException();
return Substrat.At(Substrat.Count - 1);
}
public int Pop()
{
int valeur = Peek();
Substrat.RemoveAt(Substrat.Count - 1);
return valeur;
}
}
Le code de la classe suit :
class PileVideException : Exception { }
class Pile
{
class Noeud
{
public int Valeur { get; init; }
public Noeud Prédécesseur { get; set; }
public Noeud(int valeur)
{
Valeur = valeur;
Prédécesseur = null;
}
}
Noeud Tête { get; set; }
public Pile()
{
Tête = null;
}
public bool EstVide { get => Tête == null; }
public void Push(int valeur) // en français : Empiler
{
Noeud p = new(valeur);
p.Prédécesseur = Tête;
Tête = p;
}
public int Peek()
{
if (EstVide)
throw new PileVideException();
return Tête.Valeur;
}
public int Pop()
{
int valeur = Peek();
Tête = Tête.Prédécesseur;
return valeur;
}
}