Barre de progression animée

Cette semaine, c'est Julien qui vous propose un #KataOfTheWeek : Barre de progression animée

Briefing du Kata : On retrouve souvent des barres de progression… y compris en ligne de commande. L'idée de ce kata est de créer votre propre barre de progression animée en ligne de commande avec un pourcentage. Les étapes de l'animation se font comme ceci :

==================\ - 10%
==================| - 10%
==================/ - 10%
==================- - 10%

// Plus tard
======================\ - 15%

Bien évidemment vous devez à chaque fois rester sur la même ligne, sinon ce n'est pas drôle. ;-)

Je vous ai indiqué les méthodes que la classe de barre de progression doit impérativement inclure, ainsi qu'un petit petit programme de test :

public class MyProgressBar {

  public MyProgressBar(final int nbSteps, final int lineLength) {
    // nbSteps : le nombre d'étapes de la barre de progression
    // lineLength : la longueur max de la barre une fois qu'on est à 100%
  }

  public void increment() {
    // C'est LA méthode la plus importante, car c'est celle que votre programme appelle pour
    // rafraîchir l'affichage.
  }

  public static void main(String[] args) throws Throwable {
    var nbSteps = 1000;

    var bar = new MyProgressBar(nbSteps, 50);

    for (var i = 0; i < nbSteps; i++) {
      bar.increment();
      Thread.sleep(100L);
    }
  }
}

Important : le programme ne fonctionnera pas dans un navigateur. Il vaut l'exécuter à la ligne de commande sous Linux, macOS ou Windows. Pour vous en convaincre vous pouvez essayer les animations sous Jenkins par exemple.

Astuce : pensez au caractère retour arrière.

À vous de jouer !

Saurez-vous résoudre le problème ?

Bon courage !


Et voici une solution proposée par l'auteur en Java :

import java.util.*;

public class MyProgressBar {

  private static final List<String> STEPS = Collections.unmodifiableList(Arrays.asList("|", "\\", "-", "/"));
  private static final String COMPLETED_STEPS_CHAR = "=";
  private static final int PERCENT_FIELD_SIZE = 10;

  private final int nbSteps;
  private final int lineLength;
  private final int totalLineLength;
  private int currentCount;

  public MyProgressBar(final int nbSteps, final int lineLength) {
    if (nbSteps <= 0 || lineLength <= 0) {
      throw new IllegalArgumentException("nbSteps and lineLength must be strictly positive");
    }
    this.nbSteps = nbSteps;
    this.lineLength = lineLength;
    this.totalLineLength = lineLength + PERCENT_FIELD_SIZE;
    this.currentCount = 0;
  }

  private void clearLine() {
    for (int i = 0; i < totalLineLength; i++) {
      System.out.print("\b");
    }
  }

  private String getCompletedStepPart() {
    var sb = new StringBuilder();
    int completedSteps = (currentCount * lineLength) / nbSteps;
    for (var i = 0; i < completedSteps; i++) {
      sb.append(COMPLETED_STEPS_CHAR);
    }
    return sb.toString();
  }

  private String getCurrentStepPart() {
    int currentStepProgress = currentCount % nbSteps;
    int stepCharIndex = currentStepProgress % STEPS.size();
    return STEPS.get(stepCharIndex);
  }

  private String getPercentage() {
    int percent = ((currentCount + 1) * 100) / nbSteps;
    return percent + "%";
  }

  private boolean isComplete() {
    return currentCount >= (nbSteps - 1);
  }

  public void increment() {
    if (isComplete()) {
      return;
    }

    clearLine();
    var line = getCompletedStepPart() + getCurrentStepPart() + " - " + getPercentage();
    System.out.print(line);

    currentCount++;
    if (isComplete()) {
      System.out.println();
    }
  }

  public static void main(String[] args) throws Throwable {
    var nbSteps = 1000;

    var bar = new MyProgressBar(nbSteps, 50);

    for (var i = 0; i < nbSteps; i++) {
      bar.increment();
      Thread.sleep(100L);
    }
  }

}

Votre équipe TakiVeille