Utilisation du type de variables "Decimal"

« Decimal » a été créé pour avoir une précision plus grande, mais dans une plage de valeurs plus restreinte.
Il est préconisé dans les applications monétaires, tandis que les autres types à virgules flottantes (double, single) sont destinés aux calcules scientifiques (et ils ont une vitesse de traitement plus rapide).
Fait amusant :

Decimal numerateur = 1m;
Decimal denominateur = 3m;
Console.WriteLine(numerateur / denominateur * denominateur);

Le résultat sera 0.999999… Ceci parce que Decimal est justement trop précis !
L’utilisation de Round permettra de profiter de la précision de Decimal, tout en faisant les arrondis nécessaires. Comme cité dans cette ressource : « Le type Decimal n’élimine pas la nécessité d’arrondir. Il réduit plutôt des erreurs en raison de l’arrondi ».

Console.WriteLine(Math.Round(numerateur / denominateur * denominateur, 2));

Ceci retournera 1.00.
L’utilisation des autres flottants n’est pas conseillé. Car les erreurs d’arrondis vont plus faciement s’accumuler à fur et à mesure des calculs.
Cet exemple ci-dessous ne doit pas fausser l’opinion des développeurs :

Double numerateur = 1f;
Double denominateur = 3f;
Console.WriteLine(numerateur / denominateur * denominateur);

Le résultat sera de 1 ! Mais cela reflète simplement un manque de précision de la division avec ce type de flottants : numerateur / denominateur = 0.3333343
 

Snake7 : Algorithme du mouvement du serpent

Voici comment j’ai développé le mouvement du serpent dans Snake7. Pour rappel, le serpent va dans toutes les directions de manière fluide (à l’inverse du serpent classique qui va de « carré en carré » dans 4 directions).
[youtube=http://www.youtube.com/watch?v=CbzXohlMvMc]
Situation de base : Le serpent est constitué de plusieurs tronçons et de la tête. Le joueur contrôle uniquement le déplacement de la tête, et les tronçons doivent suivre.
Problématiques : 

  • Un tronçon ne connait que le tronçon qui le précède. Il n’y a pas d’intérêt à ce qu’il connaisse la position ou le comportement de la tête.
  • Pas question non plus qu’un tronçon suive exactement le même chemin que le tronçon précédent (A l’instant t, la position du tronçon x ne sera pas la position du tronçon x-1 à l’instant t-1) : Cela donnerai un mouvement trop rigide !

Solution : A chaque instant, un tronçon doit toujours se trouver la même distance du tronçon précédent. Si le tronçon précédent s’éloigne, le tronçon doit s’en approcher par le chemin le plus court.
Voici un schéma de la structure du serpent , ici composé de 4 tronçons :

… où tous les segments rouges sont de la même longueur.
Scénario 
Prenons 2 tronçons A et B, séparés de la longueur « d ». B se déplace, A doit suivre et venir se « coller » à B en gardant la même distance « d » :

Comment déterminer le chemin que doit prendre A ? Schématisons un peu mieux la situation :

d, x1 et y1 sont connus ! Reste à connaitre x2 et y2 (déplacement horizontal et vertical de A)
Et bien pour cela, on ressort nos bons vieux théorèmes de Pythagore et Thalès !

Snake7

MAJ septembre 2011 : MAJ pour Mango.
Depuis le 1er juin, mon nouveau jeu « Snake7 » est disponible sur le MarketPlace WP7 !
Il a reçu de très bonnes critiques des joueurs (après un début difficile à cause du contrôle au « joystick »).
C’est un jeu de serpent classique (dans le principe), mais moderne (dans le contrôle et les mouvements du serpent).
Je voulais vraiment changer du déplacement classique « haut, bas, gauche, droite » linéaire. Au lieu de cela, le serpent bouge dans tous les sens. Toutes les 15 secondes, une alarme retentit, et voilà les murs mouvants qui arrivent. Il faut alors attraper toutes les pommes bonus pour avoir des points.
Nouveauté de développement : les points sont enregistrés en ligne, ce qui permet d’avoir un classement des joueurs mondial.

ASP.NET : Renvoyer un ContentType différent (excel, CSV, doc…)

ASP.NET : Renvoyer un ContentType différent (C#)
Quelques conseils quand une page renvoie un flux différent que du  "text/html" :

  • Préciser l’encoding : Response.ContentEncoding = System.Text.Encoding.Default;
  • Générer le contenu dans le Page_Load, et finir par : Response.End();
  • Exemple :

 protected void Page_Load(object sender, EventArgs e)
{

Response.Clear();
// Type de contenu de la réponse
Response.ContentEncoding = System.Text.Encoding.Default;
Response.Charset = "iso-8859-1";
Response.ContentType = "text/csv";

// Entête
Response.AddHeader("Pragma", "no-cache");
        // Force le navigateur à faire un "enregistrer sous..."
Response.AddHeader("Content-Disposition", "attachment; filename="fichier.csv"");
// Corp de la réponse
Response.Write("bla bla bla");
// Pas de cache
Response.CacheControl = "no-cache";
Response.Expires = -1;
Response.End();

}

NB : Dans le corps de la réponse, pour faire des retours à la ligne « propres », il faut utiliser : System.Environment.NewLine

.NET : Génération d'une image à la volée

I believe I can (make an image on the) flyyyyyyy !
Imaginons qu’on veuille afficher une image qui changerait d’aspect à chaque chargement de page (graphiques, clavier virtuel pour l’authentification…). Il ne serait pas pratique de stocker ces images temporaires côté serveur (il faudrait être sûr de donner un nom unique aux images pour éviter les conflits par exemple).
La solution qui suit permet d’éviter de stocker des images sur le serveur. On va créer une page ASPX qui retourne un flux de type MIME « image/*** ».
Dans la page qui affichera l’image générée, il faut une balise de la sorte :
<img src="GenerateurImage.aspx" alt="" />
La page GenerateurImage.aspx ne contient rien (ou le minimum).
Tout se passe dans la procédure page_load du code-behind (
GenerateurImage.aspx.cs) :
public void Page_Load(object sender, System.EventArgs e)
{
// On peut partir d'une bitmap existante, ou en générer une nouvelle :

           Bitmap oBit = new Bitmap(???);
Graphics g =
Graphics.FromImage(oBit)


// Ici on modifie l'image avec toutes les méthodes offertes par la classe Graphics.
// On pourrait éventuellement utiliser des paramètres de la requête (ID=...)
pour rendre la chose encore plus dynamique !
// On change le type de flux de sortie
Response.ContentType = « image/jpeg »;
// On encode (compression)
ImageCodecInfo myImageCodecInfo;
Encoder myEncoder;
EncoderParameter myEncoderParameter;
EncoderParameters myEncoderParameters;
myImageCodecInfo = GetEncoderInfo(« image/jpeg »);
myEncoder = Encoder.Quality;
myEncoderParameters = new EncoderParameters(1);
// Niveau d’encodage élévé.
myEncoderParameter = new EncoderParameter(myEncoder, 100L);
myEncoderParameters.Param[0] = myEncoderParameter;
oBit.Save(Response.OutputStream, myImageCodecInfo, myEncoderParameters);
oBit.Dispose();
// Pas de cache pour cette page.
Response.CacheControl = « no-cache »;
Response.AddHeader(« Pragma », « no-cache »);
Response.Expires = -1;
}
En bonus, la fonction qui ramène le codec :
        private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for (j = 0; j < encoders.Length; ++j)
{
if (encoders[j].MimeType == mimeType)
return encoders[j];
}
return ';
}

C# : Exécuter un batch et récupérer la sortie.


private int Run_Batch(string sBatchPath, string sBatchName, string sArgs)
{
	// Code retour du batch
    int iExitCode = 0;
    // Chemin complet du batch
    string sBatchFullPath = sBatchPath + "\" + sBatchName;
    // Configuration du batch (chemin, arguments, sorties...)
    ProcessStartInfo processInfo = new ProcessStartInfo(sBatchFullPath);
    processInfo.CreateNoWindow = true;
    processInfo.Arguments = sArgs;
    processInfo.RedirectStandardOutput = true;
    processInfo.RedirectStandardError = true;
    processInfo.UseShellExecute = false;
    // Process en soi
    Process pBatch = new Process();
    // Redirection de la sortie vers une fonction
    pBatch.OutputDataReceived += new DataReceivedEventHandler(pBatch_OutputDataReceived);
    pBatch.StartInfo = processInfo;
    pBatch.EnableRaisingEvents = true;
    // Démarrage du batch + lecture des sorties.
    pBatch.Start();
    pBatch.BeginOutputReadLine();
    pBatch.BeginErrorReadLine();
    // Synchronisation
    pBatch.WaitForExit();
    iExitCode = pBatch.ExitCode;
    // Fermeture
    pBatch.Close();
    return iExitCode;
}
protected void pBatch_OutputDataReceived(Object sender, DataReceivedEventArgs e)
{
    System.Console.Write(e.Data);
}

C# : Créer rapidement un événement

Fiche mémo pour créer rapidement un événement lancé d’une classe, et abonnement à cet event.
Etapes de création d’un événement customisé complet (C#):

  • 1- DANS LA CLASSE SOULEVANT L’EVENEMENT
  • Delegate :
    public delegate void MonHandler(object sender, MonEventArgs e);
  • L’événement lui-même:
    public event MonHandler On_QuelqueChoseSePassed;
  • Classe « MonEventArgs » :
    public class MonEventArgs : EventArgs
        {
            private int _iInformation1;
            private string _sInformation2;
            internal MFluxEventArgs(int iInfo1, string sInfo2)
            {
                _iInformation1 = iInfo1;
                _sInformation2 = sInfo2;
            }
            public int Information1
            {
                get { return _iInformation1; }
            }
            public string Information2
            {
                get { return _sInformation2; }
            }
        }
  • Fonction qui lance l’événement :
        private void FireQuelqueChoseSePassed(MonEventArgs e)
        {
            if (On_QuelqueChoseSePassed != ')
            {
                On_QuelqueChoseSePassed(this, e);
            }
        }

 

  • Maintenant, dans mon code, je vais avertir quand quelque chose se passe :

    ...
    FireQuelqueChoseSePassed(new MFluxEventArgs(15, "On est ici"));
    ...
  • 2- AILLEURS, on souhaite s’abonner à l’événement :
  • On abonne une fonction de la même signature que le delegate :

    MaClasse.On_QuelqueChoseSePassed +=new MaClasse.MonHandler(LaFonctionQuiVaRecevoirLEvent);
  • La fonction abonnée :

    protected void LaFonctionQuiVaRecevoirLEvent(object sender, MonEventArgs e)
    {
    // Ici, on peut utiliser "sender" (objet MaClasse instancié), ou "e" (qui contient un entier et un string...)
    }