wpf

L’insieme cellulare di Conway è servito: un programma educativo alla scoperta del gioco della vita

Dopo aver spiegato le regole su cui si basa il gioco della vita di Conway e più in generale degli insiemi cellulari a due dimensioni, vi presento una sua implementazione software, open-source e gratuita, sviluppato in C# (WPF .NET Framework 4). Di seguito un video con le principali funzionalità e caratteristiche:

E’ possibile impostare la regola S/B (Survivor / Born), la dimensione dell’insieme, il numero di stati e i relativi colori. E’ possibile variare il modello di rendering dei singoli automi (punti, palle, quadrati) e variarne la dimensione. Impostare una condizione iniziale in qualsiasi momento, disegnando direttamente all’interno dell’insieme. Esportare in vari formati: gif animata, PNGs, video Avi. Possibilità di importare modelli codificati nel formato RLE, e salvarli in locale utilizzando un formato proprietario.

Qui un paio di gif animate di esempio su cosa si può ottenere con il programma:
Gioco della Vita Regola S012345689/B3
Regola S012345689/B3

Gioco della Vita Regola S1234/B3
Regola S1234/B3

L’insieme cellulare di Wolfram è servito: un programma educativo alla scoperta degli automi cellulari unidimensionali

Dopo i recenti articoli pubblicati Rendering grafico in GDI e WPF e Calcolo parallelo applicato agli automi cellulari ecco un video dimostrativo sul mondo cellulare di Wolfram.

Scritto in C# WPF (.NET Framework 4) e compilato con Visual Studio 2010, il codice sorgente e l’eseguibile sono scaricabili dalla pagina cellular-automata-world ospitata su google code.

Come renderizzare automi cellulari a due dimensioni con GDI e WPF

Ok, noi abbiamo il nostro automa cellulare bidimensionale, un divertente gioco della vita interattivo implementato in C# / WPF. Vediamo come estendere il rendering grafico ad armoniose e più interessanti varianti. Definiamo prima di tutto l’interfaccia IRender:

public interface IRender {
  int CellSize { get; set; }
  void SetColorPalette(IList<System.Drawing.SolidBrush> colorsPalette);
  void SetColorPalette(IList<System.Windows.Media.Color> colorsPalette);
  System.Windows.Controls.Image Render(byte[] buf, int width, int height);
}

 
Il rendering base, basato cioè sulla corrispondenza “cella” -> “1×1 pixel su schermo” viene implementato agevolmente utilizzando BitmapImage.Create():

class RenderBase : IRender {
  public int CellSize { get { return 1; } set { } }
  private BitmapPalette Palette = BitmapPalettes.BlackAndWhite;
  public void SetColorPalette(IList<System.Drawing.SolidBrush> colorsPalette) {
    Palette = new BitmapPalette(ImageUtils.ConvertPaletteInMediaColor(colorsPalette));
  }
  public void SetColorPalette(IList<System.Windows.Media.Color> colorsPalette) {
    Palette = new BitmapPalette(colorsPalette);
  }

  public System.Windows.Controls.Image Render(byte[] buf, int width, int height) {
    const double dpi = 96;
    return new Image() {
      Source = BitmapImage.Create((int)width, (int)height, dpi, dpi,
        PixelFormats.Indexed8, Palette, buf, (int)width)
    };
  }
  public override string ToString() {
    return "1x1 Pixel (fastest)";
  }
}

 
Per ottenere diversi render più complessi, basati sulla forma della singola cella e relativamente al bordo della stessa, creiamo una classe astratta RenderGraphics che utilizza le librerie grafiche GDI+ per il disegno delle singole celle e la composizione dell’ambiente.

abstract class RenderGraphics : IRender {
  public int CellSize { get; set; }
  protected IList<System.Drawing.SolidBrush> Palette { get; private set; }

  public void SetColorPalette(IList<System.Drawing.SolidBrush> colorsPalette) {
    foreach (System.Drawing.SolidBrush brush in Palette)
      brush.Dispose();
    Palette = colorsPalette;
  }
  public void SetColorPalette(IList<System.Windows.Media.Color> colorsPalette) {
    Palette = ImageUtils.ConvertPaletteInDrawingBrush(colorsPalette);
  }

  public System.Windows.Controls.Image Render(byte[] buf, int width, int height) {
    using (Bitmap b = new Bitmap(width * CellSize, height * CellSize)) {
      using (Graphics g = Graphics.FromImage(b)) {
        g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
        g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
        g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
        for (int i = 0; i < width * height; i++) {
          int x = (int)(i % width);
          int y = (int)(i / width);
          Draw(g, Palette[buf[i]], x, y);
        }
      }

      return ImageUtils.ConvertToWpfImage(b);
    }
  }

  protected abstract void Draw(Graphics g, System.Drawing.SolidBrush brush, int x, int y);
}

 
I diversi rendering grafici si ottengono estendendo RenderGraphics e implementando il metodo Draw():

class RenderFilledRects : RenderGraphics {
  public override string ToString() { return "Filled Rects without border"; }
  protected override void Draw(Graphics g, System.Drawing.SolidBrush brush, int x, int y) {
    g.FillRectangle(brush, x + (CellSize - 1) * x, y + (CellSize - 1) * y, CellSize, CellSize);
  }
}
class RenderFilledRectsBorder : RenderGraphics {
  public override string ToString() {	return "Filled Rects with border"; }
  protected override void Draw(Graphics g, System.Drawing.SolidBrush brush, int x, int y) {
    g.FillRectangle(brush, x + (CellSize - 1) * x, y + (CellSize - 1) * y, CellSize - 1, CellSize - 1);
  }
}
class RenderFilledDots : RenderGraphics {
  public override string ToString() { return "Filled Dots without border"; }
  protected override void Draw(Graphics g, SolidBrush brush, int x, int y) {
    g.FillEllipse(brush, x + (CellSize-1) * x, y + (CellSize-1) * y, CellSize+1, CellSize+1);
  }
}
class RenderFilledDotsBorder : RenderGraphics {
  public override string ToString() { return "Filled Dots with border"; }
  protected override void Draw(Graphics g, SolidBrush brush, int x, int y) {
    g.FillEllipse(brush, x + (CellSize - 1) * x, y + (CellSize - 1) * y, CellSize-1, CellSize-1);
  }		
}
class RenderEmptyDots : RenderGraphics {
  public override string ToString() { return "Empty Dots"; }
  protected override void Draw(Graphics g, SolidBrush brush, int x, int y) {
    g.DrawEllipse(new Pen(brush, 1), x + (CellSize - 1) * x, y + (CellSize - 1) * y, CellSize - 2, CellSize - 2);
  }		
}
class RenderEmptyRects : RenderGraphics {
  public override string ToString() { return "Empty Rects"; }
  protected override void Draw(Graphics g, SolidBrush brush, int x, int y) {
    g.DrawRectangle(new Pen(brush, 1), x + (CellSize - 1) * x, y + (CellSize - 1) * y, CellSize - 2, CellSize - 2);
  }
}
game of life animated gif

Gioco della vita renderizzato a cubetti

Lo sviluppo del cudumar friulano procede spedito verso il successo!

Sono diversi mesi che seguiamo un progetto davvero interessante: cudumar-xmpp. Si tratta di un client XMPP gratuito, innovativo per la sua leggerezza e usabilità.
Nato in terra friulana da un anno e qualche mese, già si è inserito di gran prepotenza tra i client più utilizzati ed apprezzati.
Dopo una prima intervista di qualche mese fa, siamo di nuovo qui a dare la voce ad uno degli ideatori nonchè sviluppatori del progetto, in previsione dell’imminente rilascio della nuova versione del client:

Abbiamo lavorato moltissimo al supporto dello standard vCard, in modo tale da ottene informazioni sempre aggiornate dai propri contatti, visualizzarne avatar personali e impostandone di propri.

– commenta Daniele Tenero.

cudumar xmpp source code

cudumar-xmpp è sviluppato in WPF, C# .Net Framework 4

Il lavoro è stato svolto nel rispetto dei notri tre principi: leggerezza, stabilità, usabilità. Attualmente siamo in fase di test, abbiamo pianificato l’uscita della nuova release stabile a fine giugno 2012, vi terremo aggiornati!

xmpp sasl mechanisms source code

cudumar-xmpp supporta la maggior parte dei sistemi di autenticazione SASL utilizzati attualmente: ANONYMOUS, PLAIN, DIGEST-MD5, X-GOOGLE-TOKEN, X-OAUTH2, X-MESSENGER-OAUTH2, X-FACEBOOK-PLATFORM

Un’altra importante novità riguarda il supporto di nuovi meccanismi di autenticazione, per citarne alcuni: X-GOOGLE-TOKEN, X-OAUTH2, X-MESSENGER-OAUTH2. Soprattutto l’ultimo, il quale permette l’accesso al network MSN (Live Messenger) di Microsoft. In questo modo sarà possibile connettersi ad MSN anche senza il pesante e variopinto Windows Live Messenger.

Bellissima questa ultima notizia, è davvero emozionante che anche Microsoft si sia aperta a standard liberi come XMPP e che giovani friulani subito raccolgano questa opportunità per aprire nuovi orizzonti al proprio progetto.

Per finire, abbiamo l’onore di pubblicare in anteprima il changelog della prossima versione che verrà rilasciata il prossimo mese:
cudumar-xmpp changelog v0.0.3 #
- disco#info support (XEP-0030: Service Discovery)
- caps support (XEP-0115: Entity Capabilities)
- vcard-temp (XEP-0054: vcard-temp)
- vcard-temp:x:update (XEP-0153: vCard-Based Avatars)
- SASL X-GOOGLE-TOKEN authentication supported
- SASL X-MESSENGER-OAUTH2 authentication supported
- urn:xmpp:ping support (XEP-0199: XMPP Ping)

Da questa pagina ospitata su google code è possibile scaricare l’ultima versione e i “source code” del progetto: download cudumar-xmpp.

Come implementare automi cellulari unidimensionali in C#

Dopo il primo post sugli automi cellulari vediamo come implementare il tipo più semplice di automa: l’unidimensionale di Wolfram.

Iniziamo col definire la nostra struttura dati per descrivere l’ambiente dell’automa. Nel caso di automi di Wolfram avremmo bisogno di una matrice a due dimensioni (benchè l’automa venga definito come unidimesnionale abbiamo bisogno della seconda dimensione per descriverne l’evoluzione nel tempo).

public class Environment2D {
  public byte[] Buffer { get; private set; }
  public int Width { get; private set; }
  public int Height { get; private set; }
  public Environment2D(int w, int h) {
    Width = w;
    Height = h;
    Buffer = new byte[w * h];
  }
  public byte GetValue(int x, int y) {
    int i = x + Width * y;
    return Buffer[i];
  }
  public void SetValue(int x, int y, byte value) {
    int i = x + Width * y;
    Buffer[i] = value;
  }
}

Definiamo ora la struttura del nostro automa tramite una classe astratta. E’ una definizione generica che sarà valida anche per automi bidimensionali (come per esempio il famoso gioco della vita di Conway).

public abstract class Automata {
  public Environment2D Env { get; protected set; }
  protected Environment2D Env0 { get; set; }

  public Automata() { Env = new Environment2D(0, 0); Env0 = null; }		
  public int Width { get { return Env.Width; } }
  public int Height { get { return Env.Height; } }
  public abstract bool IsFinite();
  public abstract bool Next();
  public abstract void RandomInit();
  public abstract void StandardInit();
  public virtual void SetSize(int w, int h) { Env = new Environment2D(w, h); }

  protected abstract byte ComputeRule(int x, int y);
}

public enum AutomataInitialCondition {
  Random, Standard
}

L’automa va inizializzato di dimensioni tramite il metodo SetSize(int w, int h), vanno impostate le condizioni iniziali, che possono essere generate casualmente (RandomInit()) oppure attivando solo la cella centrale dell’automa (StandardInit()). Next() calcola e attualizza la nuova generazione, applicando la regola definita in ComputeRule(int x, int y).

Vediamo ora come implementare il caso specifico dell’automa unidimensionale di Wolfram.
Mi preme introdurre la definizione di notazione di Wolfram, che potete dolcemente leggervela su Wikipedia.

public class Automata1DWolfram : Automata {
  private int x = 0;
  private int y = 0;
  private byte rule;
  private byte[, ,] tt = new byte[2, 2, 2];
  public Automata1DWolfram(byte rule) {
    this.rule = rule;
    tt[0, 0, 0] = (byte)((rule & 1) > 0 ? 1 : 0);
    tt[0, 0, 1] = (byte)((rule & 2) > 0 ? 1 : 0);
    tt[0, 1, 0] = (byte)((rule & 4) > 0 ? 1 : 0);
    tt[0, 1, 1] = (byte)((rule & 8) > 0 ? 1 : 0);
    tt[1, 0, 0] = (byte)((rule & 16) > 0 ? 1 : 0);
    tt[1, 0, 1] = (byte)((rule & 32) > 0 ? 1 : 0);
    tt[1, 1, 0] = (byte)((rule & 64) > 0 ? 1 : 0);
    tt[1, 1, 1] = (byte)((rule & 128) > 0 ? 1 : 0);
  }
  public override bool IsFinite() {	return true; }
  public override void SetSize(int w, int h) { Env = new Environment2D(w, h);	}
  public override void RandomInit() {
    Random rand = new Random(Environment.TickCount);
    for (x = 0, y = 0; x < Env.Width; x++)
      Env.SetValue(x, y, (byte)rand.Next(0, 2));
    x = 0;
    y = 1;
  }
  public override void StandardInit() {
    Env.SetValue(Env.Width / 2, 0, 1);
    x = 0;
    y = 1;
  }
  public override bool Next() {
    for (int i = 0; i < Env.Width; i++) {
      if (y >= Env.Height)
        return false;

      Env.SetValue(x, y, (byte)(ComputeRule(x, y)));
      if (x == Env.Width - 1) {
        x = 0;
        y++;
      } else
        x++;
    }

    return true;
  }		
  protected override byte ComputeRule(int x, int y) {
    byte l = Env.GetValue(x == 0 ? 0 : x - 1, y - 1);
    byte c = Env.GetValue(x, y - 1);
    byte r = Env.GetValue(x == Env.Width - 1 ? x : x + 1, y - 1);
    return tt[l, c, r];
  }
  public override string ToString() {
    return string.Format("Wolfram Rule {0}", rule);
  }
}

L’automa è ben che implementato in tutta la sua gustosità, il parametro rule passato nel costruttore Automata1DWolfram(byte rule) definisce la regola che identifica univocamente l’automa cellulare di Wolfram. Un po’ come una legge universale che decide la vita e la morte degli esemplari di una popolazione, un po’ come un Dio che determina le sorti di un mondo immaginario ma ben regolato da leggi fisiche e strutturato da fondamenti matematici. Per ognuna delle otto possibili configurazioni dell’intorno viene deciso (la regola!) se la cella dovrà vivere o morire.

Wolfram Regola 30

L'immagine visualizza la regola 30 di Wolfram (30 = 00011110 in decimale).

In WPF, utilizzando una UniformGrid, è semplice quanto banale creare una vista d’insieme degli 256 possibili automi cellulari di Wolfram, vi allungo qualche immagine direttamente dal mio schermo.

Wolfram Rules 1 Cell Initial Condition

Le 256 regole di Wolfram, con una cella attiva come condizione iniziale.

Wolfram Rules with Random Initial Condition

Le 256 regole di Wolfram, con condizione iniziale casuale.



Ho caricato l’eseguibile e i sorgenti del progetto in questa pagina: cellular-automata-world. Se qualcuno volesse contribuire, si faccia avanti! ;=)

Cudumar-xmpp, un nuovo client gratuito e leggero conforme allo standard jabber/xmpp

Oggi presentiamo un innovativo software per accedere ai network XMPP: cudumar-xmpp. Gratuito, leggero, 100% conforme allo standard XMPP. Il nome buffo deriva dalle origini geografiche degli ideatori, friulani DOC e con uno spiccato amore per le verdure. Infatti cudumar in friulano significa cetriolo, un nome decisamente buffo da assegnare ad un programma!

cudumar-xmpp versione 0.0.2

cudumar-xmpp versione 0.0.2

Ho avuto il piacere di incontrare di persona Daniele Tenero, uno degli ideatori.

La domanda di rito: come nasce questo nome… “cudumar”?
A me piaceva “cudumar & verzutin”. Hai mai fatto una minestra di cetrioli e verze? Spettacolare… dovresti provarla. Fai bollire due cetrioli grossi a piacere in un litro d’acqua, dagli solo una lavata sotto acqua corrente ma non togliere la buccia! E’ la parte più buona, i cetrioli non vanno pelati. Aggiungi metà verza e lascia riposare per qualche minuto. Frulla il tutto con un mixer ad immersione, aggiungi sale, pepe se vuoi anche una cipolla che fa sempre bene e ti leccherai i baffi.
Volevamo un nome simpatico, che ricordasse la nostra friulanità, l’amore per le verdure, lo stile di vita semplice e casereccio. E così è anche cudumar-xmpp, semplice e anche un po’ contadino, senza tanti fronzoli un po’ come la gente friulana, che mantiene ancora un forte legame con la terra e la natura.

Parlaci di questo progetto. Perché è nato?
Inizialmente voleva solo essere un esercizio di stile, ne più ne meno. Ci interessiamo da tempo di protocolli e standard aperti sia per lavoro che per passione, è una filosofia che ci piace e intriga moltissimo. Tutto è nato dalla necessità di approfondire come lavora il server Jabber di Google Talk per valutarne possibili interazioni con client esterni. Soprattutto per quanto riguarda la trasmissione audio vocale, le chiamate VoIP, i flussi video. Un bel lavoro di reverse engineering che ci ha portati a conoscere nel dettaglio lo standard xmpp, le sue estensioni ma anche molte variazioni “fuori standard”. E’ nato per passione, perchè a noi piace impastarci il cervello, tenerci sempre in movimento, e questi risultati ci ripagano con grande soddisfazione.

Dopo un approccio filosofico buttiamoci sul tecnico. Quali tecnologie adotta cudumar-xmpp?
Le ultime e più innovative sul mercato: WPF .Net Framework 4. Con WPF si semplifica enormemente tutta l’analisi e lo sviluppo dell’interfaccia utente. L’utilizzo della versione 4 del .Net Framework permette di sfruttare tutti i vantaggi di un linguaggio di alto livello e di particolari funzionalità. Esempio: LINQ to XML ci ha permesso di semplificare notevolmente l’implementazione del protocollo XMPP, che per chi non lo sapesse è basato su XML.

Un progetto nato da poco ma già sulla bocca di tutti, se ne parla molto nelle comunità online. Quali sono i piani di sviluppo futuri?
Abbiamo puntato molto sul supporto alla chat di Facebook, con l’implementazione di un nuovo meccanismo SASL (DIGEST-MD5) per l’autenticazione ai servizi facebook. Attualmente, con questa nuova release, cudumar-xmpp è in grado di collegarsi a qualsiasi server jabber che supporti SASL PLAIN / TLS / SASL DIGEST-MD5. Abbiamo in programma di supportare altri meccanismi di autenticazione per estendere il supporto alla totalità dei servizi jabber in rete. Oltre questo aspetto tecnico ci preme il restiling grafico del logo, stiamo valutando diverse proposte grafiche da persone altamente fantasiose.

Un cetriolo parlante quindi?
Potrebbe essere.

Ultima domanda, ma non la meno importante: dove ti troviamo in rete?
Esiste un blog del progetto dove è possible restare sempre aggiornati sulle ultime novità e ultime releases, una pagina su facebook che rende il tutto molto social, la pagina di progetto per gli sviluppatori ospitata su google code dove è possibile contribuire e scaricare l’ultima versione del programma.
Cudumar Blog
Cudumar su Facebook
Cudumar su Google Code
Cudumar su Google Plus