Série 9 – Art ASCII : corrigé

Introduction

Le code du corrigé est disponible sous la forme d'une archive Zip, qui contient également le code de l'énoncé. Les solutions aux différents exercices sont brièvement discutées ci-dessous.

Visibilité des classes

Les classes représentant les images ASCII de base, les transformations (décorateurs) et les compositions (composites) n'ont pas été définies comme publiques et sont donc uniquement visibles dans leur paquetage. L'idée est que l'utilisateur de cette bibliothèque ne devrait interagir avec elle qu'au travers de l'interface ASCIImage, sans jamais utiliser directement les classes.

Cette approche est identique à celle de l'interface Stream de Java, par exemple.

Exercice 1 : images de base

La classe FromString permet d'adapter une chaîne de caractères pour en faire une image ASCII. Sa définition est triviale. Notez toutefois l'utilisation de la méthode singletonList de Collections, qui simplifie l'écriture de la méthode drawing.

La classe Filled est aussi simple que la classe FromString. Là aussi, notez que l'utilisation judicieuse des méthode nCopies de Collections et Strings permet de simplifier l'écriture du code.

Exercice 2 : transformations

La classe FlippedHorizontally transforme une image par symétrie horizontale. Cette symétrie se fait simplement en inversant chacune des chaînes composant le dessin de l'image à transformer.

La classe Transposed transforme une image par transposition. Cette transposition se fait en créant un bâtisseur de chaîne par ligne de l'image transposée, puis en parcourant les lignes de l'image à tranposer, ajoutant chaque caractère au bâtisseur correpondant. Cela fait, les chaînes des bâtisseurs sont obtenues au moyen de la méthode toString de chacun des bâtisseurs.

Exercice 3 : compositions

La classe LeftOf compose deux images en plaçant la première à gauche de la seconde. La largeur d'une telle image composite est bien entendu la somme des largeurs des deux images composées, sa hauteur celle de la plus haute d'entre-elles. Le dessin se fait relativement facilement puisqu'il suffit de concaténer deux à deux les lignes des images à composer, en prenant soin de bien traiter le cas où une image est moins haute que l'autre. Dans ce cas, il faut remplacer ses lignes manquantes par des lignes vides (composées d'espaces) de la bonne largeur.

La classe Above compose deux images en plaçant la première au sommet de la seconde. Elle est symétrique par rapport à LeftOf, c'est-à-dire que sa largeur est celle de la plus large des images composées, sa hauteur leur somme. Le dessin consiste en les lignes de la première image suivies de celles de la seconde, chaque ligne étant au besoin remplie avec des espaces pour avoir la largeur requise.

Exercice 4 : dessin d'un échiquier

Le dessin d'un échiquier peut se faire de manière relativement simple en exploitant sa symétrie. Dans un premier temps, on définit deux images rectangulaires représentant respectivement une case noire et une case blanche :

ASCIImage black = ASCIImage.filled(SIZE + 1, SIZE, '#');
ASCIImage white = ASCIImage.filled(SIZE + 1, SIZE, ' ');

Cela fait, on place côte à côte ces deux cases pour obtenir une paire noir/blanche et, par symétrie horizontale, une paire blanche/noire :

ASCIImage bw = black.leftOf(white);
ASCIImage wb = bw.flippedHorizontally();

Au moyen de ces deux paires symétriques, il est possible de définir un échiquier de deux cases de côté :

ASCIImage board2 = bw.above(wb);

En combinant quatre de ces échiquiers, on en obtient un de quatre cases de côté. Finalement, en combinant quatre échiquiers de quatre cases de côté, on obtient l'échiquier de huit cases de côté, que l'on encadre au moyen de la méthode framed (définie plus bas) puis imprime :

ASCIImage board4 = (board2.leftOf(board2))
    .above(board2.leftOf(board2));
ASCIImage board8 = (board4.leftOf(board4))
    .above(board4.leftOf(board4));
board8.framed().printOn(System.out);

Encadrer une image peut se faire très facilement en combinant les cinq images suivantes : les deux images de la ligne du haut et du bas (identiques, nommées tb ci-dessous), les deux images des colonnes de gauche et de droite (identiques, nommées lr ci-dessous) et l'image à encadrer.

default public ASCIImage framed() {
    ASCIImage tb =
        fromString('+'+ Strings.nCopies(width(), '-') +'+');
    ASCIImage lr =
        fromString(Strings.nCopies(height(), '|'))
        .transposed();
    return tb.above(lr.leftOf(this.leftOf(lr)).above(tb));
}