Comment gagner les Clash of Code Taille de Code en PHP ?

PHP ne serait apparemment pas le bon bon langage pour écrire un algorithme avec le moins de caractères possibles… On va voir que ce n’est pas tout à fait vrai !

Ici chez Tainix, si tu as lu notre manifeste, tu sais qu’on est partisan d’un code de qualité, explicite, qui respecte les standards, etc. Mais de temps en temps, un petit Clash of Code « Taille de code », c’est rigolo, ça détend et ça permet de jouer avec les limites du langage.

C’est quoi un Clash of Code « Taille de Code » ?

Appelé aussi « Code Golf« , C’est un challenge de programmation, disponible sur la plateforme CodinGame, qui consiste à trouver la solution à un problème souvent pas très compliqué, mais… pour gagner, il ne faut pas être le plus rapide, il faut être celui qui va écrire la solution avec le moins de caractères possibles… Une dizaine de langages sont disponibles pour proposer une solution. Le classement ne tient pas compte du choix du langage. On pourrait alors penser que certains langages sont avantagés…

En PHP on peut gagner ?

PHP est souvent défini comme un langage « verbeux », c’est à dire qu’il faut beaucoup de caractères pour réaliser des instructions, certains noms de fonctions sont longs, etc. D’autres langages, comme Python ou Ruby, sont moins verbeux. Celles et ceux qui gagnent ces challenges choisissent donc souvent Python ou Ruby… Mais PHP n’a pas dit son dernier mot ! Et on peut aussi gagner en PHP, la preuve en image :

#1 on réfléchit à la solution pour optimiser le code

Dans l’exemple ci-dessus, l’énoncé était le suivant :

A la fête foraine, il y a un jeu avec des ballons à percer, 18 au maximum. Si on en perce 12 ou moins, on gagne 0 ticket, si on en perce 13 ou 14, on gagne 1 ticket, si on perce 15 ou 16 on gagne 2 tickets et si on en perce 17 ou 18 on gagne 3 tickets. On a en entrée le nombre de ballons percés, il faut retourner le nombre de tickets gagnés.

Une solution claire qui reprend l’énoncé :

<?php
$n = fread(STDIN, 2); // Mécanique Coding Game
if ($n <= 12) {
   $t = 0;
} elseif ($n <= 14) {
   $t = 1;
} elseif ($n <= 16) {
   $t = 2;
} else {
   $t = 3;
}

echo $t;
?>

On pourrait améliorer le nom des variables, utiliser un switch à la place des if/else successifs, etc. mais ça fonctionne correctement.

En réfléchissant un peu on peut utiliser un tableau de correspondances :

<?php
$n = fread(STDIN, 2);
$t = [13 => 1, 14 => 1, 15 => 2, 16 => 2, 17 => 3, 18 => 3];
echo $t[$n] ?? 0;
?>

En utilisant l’opérateur null coallescent, on a pas besoin de gérer le cas 0, ce sera un peu le cas « par défaut ».

Et maintenant les astuces pour tout condenser et gagner des caractères :

  • On supprime la balise finale
  • On retire tous les espaces
  • On réduit la première balise
  • Ici $t ne sert à rien, on peut directement intégrer le tableau à la suite du echo
  • On met tout sur une ligne

Et voilà :

<?$n=fread(STDIN,2);echo[13=>1,14=>1,15=>2,16=>2,17=>3,18=>3][$n]??0;

Il faut garder en tête que dans un challenge « Taille de code », on a le temps ! Le premier algorithme trouvé pour résoudre le problème peut souvent être optimisé.

#2 enchainements d’appels et ternaire

Un autre exemple avec un énoncé un peu plus compliqué.

On a à disposition un tableau de nombres. Il faut faire la somme des éléments du tableau, sauf que si un nombre est présent dans le tableau un nombre de fois pair, il faut le déduire de la somme et non l’ajouter.

  • [100, 150] => 250
  • [100, 100] => -200

Une solution claire qui reprend l’énoncé :

<?php
// Mécaniques Coding Game :
fscanf(STDIN, "%d" , $N); // Le nombre d'éléments dans le tableau (ne servait à rien)
$values = explode(' ', fgets(STDIN)); // Les valeurs du tableau

$sum = 0;
$occurences = array_count_values($values);

foreach ($occurences as $value => $occurence) {
   if ($occurence % 2 === 0) {
      $sum -= $occurence * $value;
   } else {
      $sum += $occurence * $value;
   }
}

echo $sum;
?>

Un peu d’explications : array_count_values me permet de récupérer le nombre d’occurrences des valeurs dans un tableau. Avec mes 2 exemples ci-dessus, j’obtiens :

  • [100, 150] devient [100 => 1, 150 => 1]
  • [100, 100] devient [100 => 2]

J’initialise une somme à 0. Je parcours le tableau. Selon le nombre d’occurrences je vais ajouter ou soustraire la valeur autant de fois qu’elle est présente.

Pour réduire la taille de mon code, je vais directement passer dans le foreach le résultat de mes fonctions, je n’ai pas besoin de créer de variables intermédiaires. Et je vais utiliser un ternaire à la place de mon if.

Première réduction :

<?php
// Mécaniques Coding Game :
fscanf(STDIN, "%d" , $N); // Le nombre d'éléments dans le tableau (ne servait à rien)

$s = 0;
foreach (array_count_values(explode(' ',fgets(STDIN))) as $v => $o) {
   $s += ($o % 2 === 0 ? -$v * $o : $v * $o;
}

On peut réduire encore le ternaire en factorisant la multiplication par $o, ce qui nous donne :

<?fscanf(STDIN,"%d",$N);$s=0;foreach(array_count_values(explode(' ',fgets(STDIN)))as$v=>$o)$s+=$o*($o%2==0?-$v:$v);echo$s;

Encore une fois, on retire tous les espaces qu’on peut retirer. Il s’avère qu’on peut retirer la plupart des espaces qui se trouvent avant le signe $.

Pour ce challenge, j’avoue, je n’ai pas gagné, Python a réussi en moins de caractères. Les termes « foreach » et « array_count_values » sont trop verbeux. Et les « fscanf… » et « fgets… » sont plus courts en Python ou Ruby également (pour le coup c’est déloyal :p 🙂 ).

Résumé des astuces pour réduire la taille de son code en PHP

  • On retire tous les commentaires
  • Les variables n’ont pas besoin de plus d’une lettre
  • Pour les balises PHP, on ne conserve que « <? » au début
  • On retire tous les espaces qu’on peut
  • On évite les variables intermédiaires
  • On utilise des ternaires
  • On utilise des tableaux
  • On applique des astuces mathématiques :
    • a + 0 = a
    • a – b = a + (-b)
  • On peut utiliser la conversion de type :
    • (int) true donne 1
    • (int) false donne 0
  • On utilise des opérateurs condensés : ++, +=, **, ??, etc.

Encore une fois, ce type de challenge a pour but de « jouer » avec les limites du langage. On peut coder de cette façon quand on cherche à débugger une portion de code ou à tester un algorithme rapidement. Donc pour un code temporaire et non pour un code qui restera durablement dans un projet.

Connaitre les limitations de son langage et certains cas particuliers associés peut aussi permettre d’identifier et résoudre des bugs plus efficacement.

Pour aller + loin

Les opérateurs en PHP

Contenus en lien avec les techniques utilisées :

Sinon, n’hésite pas à choisir un challenge Tainix et après l’avoir résolu dans une solution élégante et durable, essaye de trouver une solution avec le moins de caractères possibles !


Qui a codé ce superbe contenu ?

Keep learning

Autres contenus à découvrir


Ta newsletter chaque mois

Corrigés, challenges, actualités, veille technique... aucun spam.