Site Ynov non officiel, réalisé par un étudiant dans le cadre d'un Yday.

Les notions essentielles

Ce qu'on retrouve dans presque tous les projets, avec des exemples en Python, Go et JavaScript. Choisis ton langage, tous les exemples suivent. Docs officielles : Python · Go · JavaScript.

Les bases du langage

Variables et types

Une variable donne un nom à une valeur. Chaque valeur a un type : texte, entier, décimal, booléen (vrai/faux)…

nom = "Alice"        # str
age = 19             # int
moyenne = 14.5       # float
majeur = age >= 18   # bool

print(type(age))     # <class 'int'>
nom := "Alice"      // string
age := 19           // int
moyenne := 14.5     // float64
majeur := age >= 18 // bool

var score int // déclarée sans valeur : vaut 0
fmt.Printf("%T\n", age) // int
const nom = "Alice";      // string
let age = 19;             // number
const moyenne = 14.5;     // number aussi
const majeur = age >= 18; // boolean

console.log(typeof age);  // "number"

⚠️ En Go, on ne mélange pas les types : moyenne + age ne compile pas, il faut convertir avec float64(age). En JavaScript, utilise const par défaut et let seulement si la valeur change.

Conditions

Exécuter un bloc de code seulement si une condition est vraie. On teste du cas le plus restrictif au plus large.

if note >= 16:
    mention = "Très bien"
elif note >= 14:
    mention = "Bien"
elif note >= 10:
    mention = "Passable"
else:
    mention = "Ajourné"
var mention string
if note >= 16 {
	mention = "Très bien"
} else if note >= 14 {
	mention = "Bien"
} else if note >= 10 {
	mention = "Passable"
} else {
	mention = "Ajourné"
}
let mention;
if (note >= 16) {
  mention = "Très bien";
} else if (note >= 14) {
  mention = "Bien";
} else if (note >= 10) {
  mention = "Passable";
} else {
  mention = "Ajourné";
}

⚠️ Couvre tous les cas (pense au else). En JavaScript, compare toujours avec === : "5" == 5 vaut true, "5" === 5 vaut false.

Boucles

Répéter une action : un nombre de fois connu, pour chaque élément d'une liste, ou tant qu'une condition est vraie.

for i in range(5):
    print(i)             # 0 à 4

for fruit in ["pomme", "kiwi"]:
    print(fruit)

vies = 3
while vies > 0:
    vies -= 1
for i := 0; i < 5; i++ {
	fmt.Println(i) // 0 à 4
}

for _, fruit := range []string{"pomme", "kiwi"} {
	fmt.Println(fruit)
}

// Pas de while en Go : un for avec seulement une condition
vies := 3
for vies > 0 {
	vies--
}
for (let i = 0; i < 5; i++) {
  console.log(i); // 0 à 4
}

for (const fruit of ["pomme", "kiwi"]) {
  console.log(fruit);
}

let vies = 3;
while (vies > 0) {
  vies--;
}

⚠️ Si la condition ne devient jamais fausse, la boucle ne s'arrête pas. break sort de la boucle, continue passe au tour suivant. En JavaScript, for…in parcourt les indices : pour les valeurs, c'est for…of.

Fonctions

Une fonction fait une seule tâche : elle reçoit des paramètres et renvoie un résultat. Découper son programme en petites fonctions le rend lisible et testable.

def moyenne(notes):
    """Renvoie la moyenne d'une liste de notes."""
    if not notes:
        return 0
    return sum(notes) / len(notes)

print(moyenne([12, 15, 9]))  # 12.0
func moyenne(notes []float64) float64 {
	if len(notes) == 0 {
		return 0
	}
	total := 0.0
	for _, n := range notes {
		total += n
	}
	return total / float64(len(notes))
}

fmt.Println(moyenne([]float64{12, 15, 9})) // 12
function moyenne(notes) {
  if (notes.length === 0) return 0;
  const total = notes.reduce((somme, n) => somme + n, 0);
  return total / notes.length;
}

console.log(moyenne([12, 15, 9])); // 12

⚠️ Afficher n'est pas renvoyer : une fonction qui fait print au lieu de return ne peut pas être réutilisée dans un calcul. Pense aux cas limites (ici une liste vide : division par zéro).

Structures de données

Listes et tableaux

Une suite ordonnée de valeurs, accessibles par leur indice (qui commence à 0).

notes = [12, 15, 9]
notes.append(18)              # ajouter à la fin
notes.pop(2)                  # retirer l'élément d'indice 2
print(notes[0], len(notes))   # 12 3

bonnes = [n for n in notes if n >= 10]   # filtrer
doubles = [n * 2 for n in notes]         # transformer
notes := []int{12, 15, 9}
notes = append(notes, 18)         // ajouter à la fin
notes = slices.Delete(notes, 2, 3) // retirer l'élément d'indice 2
fmt.Println(notes[0], len(notes)) // 12 3

var bonnes []int // filtrer : une boucle
for _, n := range notes {
	if n >= 10 {
		bonnes = append(bonnes, n)
	}
}
const notes = [12, 15, 9];
notes.push(18);                      // ajouter à la fin
notes.splice(2, 1);                  // retirer l'élément d'indice 2
console.log(notes[0], notes.length); // 12 3

const bonnes = notes.filter((n) => n >= 10); // filtrer
const doubles = notes.map((n) => n * 2);     // transformer

⚠️ Le dernier élément est à l'indice longueur - 1 : lire au-delà plante (Python, Go) ou renvoie undefined (JavaScript). Évite d'ajouter ou retirer des éléments pendant que tu parcours la liste.

Dictionnaires et objets

Associer des clés à des valeurs : les propriétés d'un joueur, les scores par pseudo, une table de conversion…

joueur = {"nom": "Alice", "score": 0}
joueur["score"] += 10
joueur["vies"] = 3                 # nouvelle clé

print(joueur.get("bonus", 0))      # 0 si la clé n'existe pas

for cle, valeur in joueur.items():
    print(cle, valeur)
// Données de forme fixe : une struct
type Joueur struct {
	Nom   string
	Score int
}
j := Joueur{Nom: "Alice"}
j.Score += 10

// Clé -> valeur du même type : une map
scores := map[string]int{"Alice": 0, "Bob": 5}
scores["Charlie"] = 3 // nouvelle clé

if s, ok := scores["Bob"]; ok { // la clé existe ?
	fmt.Println(s)
}

for nom, s := range scores {
	fmt.Println(nom, s)
}
const joueur = { nom: "Alice", score: 0 };
joueur.score += 10;
joueur.vies = 3;                  // nouvelle propriété

console.log(joueur.bonus ?? 0);   // 0 si la propriété n'existe pas

for (const [cle, valeur] of Object.entries(joueur)) {
  console.log(cle, valeur);
}

⚠️ Lire une clé absente : erreur KeyError en Python (utilise .get()), valeur zéro en Go, undefined en JavaScript. L'ordre de parcours d'une map Go est aléatoire.

Grilles (tableaux 2D)

Une liste de lignes, chaque ligne étant une liste de cases. On accède à une case avec grille[ligne][colonne].

grille = [[" "] * 3 for _ in range(3)]  # 3 lignes, 3 colonnes
grille[1][2] = "X"                       # ligne 1, colonne 2

for ligne in grille:
    print("|".join(ligne))
var grille [3][3]string // 3 lignes, 3 colonnes, cases ""
grille[1][2] = "X"      // ligne 1, colonne 2

for _, ligne := range grille {
	fmt.Println(strings.Join(ligne[:], "|"))
}
const grille = Array.from({ length: 3 }, () => Array(3).fill(" "));
grille[1][2] = "X"; // ligne 1, colonne 2

for (const ligne of grille) {
  console.log(ligne.join("|"));
}

⚠️ [[" "] * 3] * 3 en Python (ou Array(3).fill(Array(3)) en JavaScript) crée trois fois la même ligne : modifier une case les modifie toutes. Vérifie toujours que 0 ≤ ligne < hauteur avant d'accéder à une case.

Chaînes de caractères

Nettoyer, chercher, découper et assembler du texte.

mot = "  Élève "
propre = mot.strip().lower()      # "élève"
print(len(propre), propre[0])     # 5 é
print("v" in propre)              # True
print(propre.replace("è", "e"))   # "éleve"
print("a,b,c".split(","))         # ['a', 'b', 'c']
print("-".join(["a", "b"]))       # "a-b"
mot := "  Élève "
propre := strings.ToLower(strings.TrimSpace(mot)) // "élève"
fmt.Println(utf8.RuneCountInString(propre))       // 5 (len() compte les octets : 7)
fmt.Println(strings.Contains(propre, "v"))        // true
fmt.Println(strings.ReplaceAll(propre, "è", "e")) // "éleve"
fmt.Println(strings.Split("a,b,c", ","))          // [a b c]
fmt.Println(strings.Join([]string{"a", "b"}, "-")) // "a-b"
const mot = "  Élève ";
const propre = mot.trim().toLowerCase();  // "élève"
console.log(propre.length, propre[0]);    // 5 "é"
console.log(propre.includes("v"));        // true
console.log(propre.replaceAll("è", "e")); // "éleve"
console.log("a,b,c".split(","));          // ["a", "b", "c"]
console.log(["a", "b"].join("-"));        // "a-b"

⚠️ Les chaînes ne se modifient pas : mot.lower() renvoie une nouvelle chaîne, mot reste inchangé. Normalise avant de comparer (espaces, majuscules) et attention aux accents : é n'est pas e.

Entrées et sorties

Lire et valider une saisie

Une saisie arrive toujours sous forme de texte : il faut la convertir, puis vérifier qu'elle est valide, et redemander tant qu'elle ne l'est pas.

def demander_entier(message, mini, maxi):
    while True:
        saisie = input(message).strip()
        try:
            n = int(saisie)
        except ValueError:
            print("Ce n'est pas un nombre.")
            continue
        if mini <= n <= maxi:
            return n
        print(f"Entre {mini} et {maxi} stp.")

age = demander_entier("Ton âge ? ", 1, 120)
var lecteur = bufio.NewScanner(os.Stdin)

func demanderEntier(message string, mini, maxi int) int {
	for {
		fmt.Print(message)
		if !lecteur.Scan() {
			os.Exit(1) // entrée fermée (Ctrl+D)
		}
		n, err := strconv.Atoi(strings.TrimSpace(lecteur.Text()))
		if err != nil {
			fmt.Println("Ce n'est pas un nombre.")
			continue
		}
		if n >= mini && n <= maxi {
			return n
		}
		fmt.Printf("Entre %d et %d stp.\n", mini, maxi)
	}
}

age := demanderEntier("Ton âge ? ", 1, 120)
// Node.js (fichier .mjs ou "type": "module")
import * as readline from "node:readline/promises";

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });

async function demanderEntier(message, mini, maxi) {
  while (true) {
    const saisie = (await rl.question(message)).trim();
    const n = Number(saisie);
    if (saisie === "" || !Number.isInteger(n)) {
      console.log("Ce n'est pas un nombre.");
    } else if (n >= mini && n <= maxi) {
      return n;
    } else {
      console.log(`Entre ${mini} et ${maxi} stp.`);
    }
  }
}

const age = await demanderEntier("Ton âge ? ", 1, 120);
rl.close();

⚠️ Teste les saisies bizarres : vide, lettres, négatif, très grand, espaces. En français on écrit 12,5 : remplace la virgule par un point avant de convertir un décimal. En JavaScript, Number("") vaut 0, d'où le test de la saisie vide.

Afficher et formater

Insérer des valeurs dans un texte, arrondir, aligner en colonnes.

prix = 12.5
nom = "Alice"
print(f"{nom} doit {prix:.2f} €")   # Alice doit 12.50 €
print(f"{'Score':<10}|{42:>5}")     # aligner en colonnes
print(0.1 + 0.2)                    # 0.30000000000000004
prix := 12.5
nom := "Alice"
fmt.Printf("%s doit %.2f €\n", nom, prix) // Alice doit 12.50 €
fmt.Printf("%-10s|%5d\n", "Score", 42)    // aligner en colonnes
texte := fmt.Sprintf("%.1f", 3.14159)     // formater sans afficher : "3.1"
const prix = 12.5;
const nom = "Alice";
console.log(`${nom} doit ${prix.toFixed(2)} €`); // Alice doit 12.50 €
console.log("Score".padEnd(10) + "|" + String(42).padStart(5));
console.log(0.1 + 0.2); // 0.30000000000000004

⚠️ Les nombres décimaux sont approximatifs dans tous les langages. Arrondis seulement à l'affichage, et pour de l'argent, compte en centimes avec des entiers.

État et boucle principale

L'état du programme

L'état, c'est tout ce qui décrit la situation à un instant donné : score, vies, tour, grille, en pause ou non. Regroupe-le dans une seule structure, modifiée par des fonctions dédiées.

etat = {
    "mot": "PYTHON",
    "trouvees": set(),
    "vies": 6,
}

def proposer(etat, lettre):
    if lettre in etat["mot"]:
        etat["trouvees"].add(lettre)
    else:
        etat["vies"] -= 1

def est_gagne(etat):
    return all(l in etat["trouvees"] for l in etat["mot"])
type Partie struct {
	Mot      string
	Trouvees map[rune]bool
	Vies     int
}

func (p *Partie) Proposer(lettre rune) {
	if strings.ContainsRune(p.Mot, lettre) {
		p.Trouvees[lettre] = true
	} else {
		p.Vies--
	}
}

func (p *Partie) EstGagnee() bool {
	for _, l := range p.Mot {
		if !p.Trouvees[l] {
			return false
		}
	}
	return true
}

partie := &Partie{Mot: "GOLANG", Trouvees: map[rune]bool{}, Vies: 6}
const etat = {
  mot: "JAVASCRIPT",
  trouvees: new Set(),
  vies: 6,
};

function proposer(etat, lettre) {
  if (etat.mot.includes(lettre)) {
    etat.trouvees.add(lettre);
  } else {
    etat.vies--;
  }
}

function estGagne(etat) {
  return [...etat.mot].every((l) => etat.trouvees.has(l));
}

⚠️ Une seule source de vérité : ne stocke pas une information qu'on peut calculer (le nombre de lettres trouvées se déduit de trouvees). L'affichage lit l'état, il ne le stocke pas. En multijoueur, l'état de référence est sur le serveur.

La boucle principale

Jeux, menus console, serveurs : presque tout programme interactif suit le même schéma, répété en boucle. 1. lire les entrées, 2. mettre à jour l'état, 3. afficher.

import time

while True:
    entrees = lire_entrees()          # 1. écouter le joueur
    if not mettre_a_jour(entrees):    # 2. faire évoluer l'état (False = fin)
        break
    dessiner()                        # 3. afficher
    time.sleep(1 / 60)                # environ 60 tours par seconde
ticker := time.NewTicker(time.Second / 60) // 60 tours par seconde
defer ticker.Stop()

for range ticker.C {
	entrees := lireEntrees()   // 1. écouter le joueur
	if !mettreAJour(entrees) { // 2. faire évoluer l'état (false = fin)
		break
	}
	dessiner() // 3. afficher
}
// Navigateur : requestAnimationFrame rappelle la fonction à chaque image (~60/s)
function tour() {
  const entrees = lireEntrees();     // 1. écouter le joueur
  if (!mettreAJour(entrees)) return; // 2. faire évoluer l'état (false = fin)
  dessiner();                        // 3. afficher
  requestAnimationFrame(tour);
}
requestAnimationFrame(tour);

⚠️ Sépare la logique (mise à jour) du dessin : tu pourras changer l'un sans casser l'autre. Pour un jeu, base les déplacements sur le temps écoulé depuis le dernier tour, sinon il ira plus vite sur un ordinateur rapide.

Données externes

Sauvegarder dans un fichier (JSON)

Écrire les données dans un fichier pour les retrouver au prochain lancement. JSON est un format texte lisible, compris par tous les langages.

import json

taches = [{"titre": "Réviser", "faite": False}]

with open("taches.json", "w", encoding="utf-8") as f:
    json.dump(taches, f, ensure_ascii=False, indent=2)

try:
    with open("taches.json", encoding="utf-8") as f:
        taches = json.load(f)
except FileNotFoundError:
    taches = []  # premier lancement
type Tache struct {
	Titre string `json:"titre"`
	Faite bool   `json:"faite"`
}

taches := []Tache{{Titre: "Réviser"}}

data, err := json.MarshalIndent(taches, "", "  ")
if err != nil {
	log.Fatal(err)
}
if err := os.WriteFile("taches.json", data, 0o644); err != nil {
	log.Fatal(err)
}

data, err = os.ReadFile("taches.json")
if errors.Is(err, os.ErrNotExist) {
	taches = nil // premier lancement
} else if err != nil {
	log.Fatal(err)
} else if err := json.Unmarshal(data, &taches); err != nil {
	log.Fatal(err)
}
// Node.js
import { readFile, writeFile } from "node:fs/promises";

let taches = [{ titre: "Réviser", faite: false }];

await writeFile("taches.json", JSON.stringify(taches, null, 2));

try {
  taches = JSON.parse(await readFile("taches.json", "utf-8"));
} catch (err) {
  if (err.code !== "ENOENT") throw err;
  taches = []; // premier lancement
}

// Navigateur : localStorage.setItem("taches", JSON.stringify(taches))

⚠️ Au premier lancement le fichier n'existe pas : gère ce cas. Précise l'encodage UTF-8, sinon les accents cassent sous Windows. Un chemin relatif part du dossier depuis lequel tu lances le programme, pas de celui du fichier source.

Appeler une API HTTP

Envoyer une requête à un serveur et récupérer sa réponse, le plus souvent en JSON. Exemple avec la PokéAPI.

import requests  # pip install requests

rep = requests.get("https://pokeapi.co/api/v2/pokemon/pikachu", timeout=10)
rep.raise_for_status()  # erreur si code 4xx / 5xx
pokemon = rep.json()
print(pokemon["name"], pokemon["weight"])
type Pokemon struct {
	Name   string `json:"name"`
	Weight int    `json:"weight"`
}

rep, err := http.Get("https://pokeapi.co/api/v2/pokemon/pikachu")
if err != nil {
	log.Fatal(err)
}
defer rep.Body.Close()
if rep.StatusCode != http.StatusOK {
	log.Fatalf("code HTTP %d", rep.StatusCode)
}

var p Pokemon
if err := json.NewDecoder(rep.Body).Decode(&p); err != nil {
	log.Fatal(err)
}
fmt.Println(p.Name, p.Weight)
const rep = await fetch("https://pokeapi.co/api/v2/pokemon/pikachu");
if (!rep.ok) throw new Error(`code HTTP ${rep.status}`);
const pokemon = await rep.json();
console.log(pokemon.name, pokemon.weight);

⚠️ Une API peut être lente, en panne ou répondre 404 : vérifie le code de réponse et affiche un message clair. Ne l'appelle pas en boucle (les API limitent le nombre de requêtes) : garde le résultat en mémoire.

Aléatoire

Tirer au hasard

Un nombre, un élément dans une liste, un mélange. Pour tout ce qui touche à la sécurité (mot de passe, jeton), il existe un aléatoire dédié.

import random
import secrets

de = random.randint(1, 6)  # 1 à 6 inclus
coup = random.choice(["pierre", "feuille", "ciseaux"])
cartes = [1, 1, 2, 2, 3, 3]
random.shuffle(cartes)     # mélange sur place

mdp = "".join(secrets.choice("abcdef0123456789") for _ in range(12))  # sécurité
import (
	crand "crypto/rand"
	"math/rand/v2"
)

de := rand.IntN(6) + 1 // IntN(6) donne 0 à 5
coups := []string{"pierre", "feuille", "ciseaux"}
coup := coups[rand.IntN(len(coups))]
cartes := []int{1, 1, 2, 2, 3, 3}
rand.Shuffle(len(cartes), func(i, j int) {
	cartes[i], cartes[j] = cartes[j], cartes[i]
})

mdp := crand.Text() // sécurité (Go 1.24+)
const de = Math.floor(Math.random() * 6) + 1; // 1 à 6
const coups = ["pierre", "feuille", "ciseaux"];
const coup = coups[Math.floor(Math.random() * coups.length)];

// Mélanger : algorithme de Fisher-Yates
const cartes = [1, 1, 2, 2, 3, 3];
for (let i = cartes.length - 1; i > 0; i--) {
  const j = Math.floor(Math.random() * (i + 1));
  [cartes[i], cartes[j]] = [cartes[j], cartes[i]];
}

const octets = crypto.getRandomValues(new Uint8Array(12)); // sécurité

⚠️ Vérifie les bornes : selon la fonction, la valeur maximale est incluse ou non. tableau.sort(() => Math.random() - 0.5) mélange mal, utilise Fisher-Yates.

Bonnes pratiques

Gérer les erreurs

Quand quelque chose peut échouer (saisie, fichier, réseau), on le prévoit : on signale l'erreur, et on la traite là où on sait quoi en faire.

def diviser(a, b):
    if b == 0:
        raise ValueError("division par zéro")
    return a / b

try:
    print(diviser(10, 0))
except ValueError as err:
    print("Erreur :", err)
func diviser(a, b float64) (float64, error) {
	if b == 0 {
		return 0, errors.New("division par zéro")
	}
	return a / b, nil
}

res, err := diviser(10, 0)
if err != nil {
	fmt.Println("Erreur :", err)
	return
}
fmt.Println(res)
function diviser(a, b) {
  if (b === 0) throw new Error("division par zéro");
  return a / b;
}

try {
  console.log(diviser(10, 0));
} catch (err) {
  console.log("Erreur :", err.message);
}

⚠️ N'avale jamais une erreur en silence (except: pass, catch {} vide) : le bug sera introuvable. Lis le message d'erreur en entier, il indique le fichier et la ligne.

Garder les secrets hors du code

Token de bot, clé d'API, mot de passe : on les lit depuis une variable d'environnement, jamais écrits dans le code. En local, on les met dans un fichier .env listé dans .gitignore.

import os

token = os.environ.get("DISCORD_TOKEN")
if not token:
    raise SystemExit("Variable DISCORD_TOKEN manquante")
token := os.Getenv("DISCORD_TOKEN")
if token == "" {
	log.Fatal("variable DISCORD_TOKEN manquante")
}
// Node 20+ : node --env-file=.env bot.js
const token = process.env.DISCORD_TOKEN;
if (!token) throw new Error("Variable DISCORD_TOKEN manquante");

⚠️ Un secret poussé sur GitHub est un secret volé, même supprimé ensuite : régénère-le immédiatement. Pour charger un .env : python-dotenv (Python), godotenv (Go).

Versionner avec Git

Enregistrer une version de ton projet à chaque étape qui marche. Tu peux revenir en arrière, et le mettre en ligne sur GitHub.

git init
git add .
git commit -m "feat: affichage de la grille"
git remote add origin https://github.com/ton-pseudo/mon-projet.git
git push -u origin main

⚠️ Un commit par étape, avec un message qui dit ce qui a changé. Vérifie git status avant chaque git add . pour ne pas commiter un .env.

Méthode de travail

  • Commence par la plus petite version qui marche, puis ajoute une étape à la fois.
  • Lance et teste ton programme après chaque étape, pas seulement à la fin.
  • Essaie de le casser : saisie vide, zéro, négatif, très grand, accents, double clic.
  • Ne fais jamais confiance à ce qui vient de l'extérieur (saisie, fichier, API, autre joueur) : vérifie-le. Dans une page web, affiche le texte des autres avec textContent, jamais innerHTML (faille XSS).
  • Cherche dans la documentation officielle avant de copier du code, et comprends ce que tu copies.
  • Donne des noms clairs à tes variables et fonctions : ton code doit se lire comme une phrase.