diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5b968a4eeff4264bd01ba8b0e41db2ace1f69ed9
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,17 @@
+list-build:
+  stage: build
+  image: gcc
+  script:
+    - cd Src
+    - make test_liste
+  artifacts:
+    paths:
+      - Src/test_liste
+
+list-test:
+  stage: test
+  script:
+    - Src/test_liste
+  dependencies:
+    - list-build
+
diff --git a/README.md b/README.md
index 733d2611879d510caaf093cac8de13906cb9f508..36a3a728e58e21896d493b28de25de2845d61902 100644
--- a/README.md
+++ b/README.md
@@ -141,6 +141,14 @@ l'éxécution. Vous pouvez en ajouter à votre guise.
 <a name="listes_chainees"></a>
 ## Listes chaînées
 
+Dans un premier temps, vous travaillerez sur les fichiers :
+
+* Src/cellule.hpp/cpp
+* Src/liste.hpp/cpp
+* Src/test_liste.hpp/cpp
+
+Un Makefile vous est fourni et préparé pour compiler le code.
+
 Une liste chaînée est une structure de données dont le but est de stocker une
 séquence de valeurs. La liste est composée d'un ensemble de *cellules*. Chaque
 cellule contient une valeur, ainsi que l'adresse de la cellule suivante. Une
@@ -316,6 +324,10 @@ sont adaptées pour réaliser un jeu de *serpent*. Une base vous est fournie, à
 vous de l'étendre pour ajouter l'apparition de bonus à manger, des niveaux plus
 complexes, l'éventuel changement de niveau, le score, ...
 
+Vous pouvez désormais commencer à examiner les autres fichiers. Le fichier
+contenant le programme principal du jeu est `jeu_serpent.cpp`. Sa compilation
+est réalisée via `make jeu_serpent`.
+
 <a name="rappels"></a>
 ## Rappels
 
diff --git a/Src/Makefile b/Src/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..984f3b48b72fe2386f45771b2da885757b658ce1
--- /dev/null
+++ b/Src/Makefile
@@ -0,0 +1,60 @@
+CXX      = g++
+
+.DEFAULT_GOAL := test_liste
+
+SOURCES = 
+SOURCES += cellule.cpp
+SOURCES += liste.cpp
+
+OBJECTS = $(SOURCES:.cpp=.o)
+
+CXXFLAGS  += -g -Wall -std=c++11 -pedantic
+LDFLAGS +=
+
+$(OBJECTS) : %.o : %.cpp
+	$(CXX) -MMD $(CXXFLAGS) -c $< -o $@
+
+CLEAN_OBJECTS = $(OBJECTS)
+TARGETS = 
+
+########## test_liste ##########
+
+LISTE_SOURCES = test_liste.cpp
+LISTE_OBJECTS = $(LISTE_SOURCES:.cpp=.o)
+
+test_liste : $(LISTE_OBJECTS) $(OBJECTS) $(HEADERS)
+	$(CXX) $(LISTE_OBJECTS) $(OBJECTS) -o $@ $(LDFLAGS)
+	
+$(LISTE_OBJECTS): %.o : %.cpp
+	$(CXX) -MMD $(CXXFLAGS) -c $< -o $@
+
+all : test_liste
+TARGETS += test_liste
+CLEAN_OBJECTS += $(LISTE_OBJECTS)
+
+########## serpent ##########
+
+SERPENT_SOURCES = coordonnees.cpp
+SERPENT_SOURCES += niveau.cpp
+SERPENT_SOURCES += serpent.cpp
+SERPENT_SOURCES += jeu_serpent.cpp
+
+SERPENT_OBJECTS = $(SERPENT_SOURCES:.cpp=.o)
+
+jeu_serpent : $(SERPENT_OBJECTS) $(OBJECTS) $(HEADERS)
+	$(CXX) $(SERPENT_OBJECTS) $(OBJECTS) -o $@ $(LDFLAGS) -lncurses
+	
+$(SERPENT_OBJECTS): %.o : %.cpp
+	$(CXX) -MMD $(CXXFLAGS) -c $< -o $@
+
+all : jeu_serpent
+TARGETS += jeu_serpent
+CLEAN_OBJECTS += $(SERPENT_OBJECTS)
+
+########## cleanup ##########
+DEPS = $(CLEAN_OBJECTS:.o=.d)
+
+clean:
+	@rm -f $(DEPS) $(TARGETS) $(CLEAN_OBJECTS)
+
+-include $(DEPS)
diff --git a/Src/cellule.cpp b/Src/cellule.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8a9573d0ee5cec99e0912969ca4d96a09976a23e
--- /dev/null
+++ b/Src/cellule.cpp
@@ -0,0 +1,3 @@
+#include "cellule.hpp"
+
+
diff --git a/Src/cellule.hpp b/Src/cellule.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9b25d6435cdbbbd3315de8c840c616c0f1c6467f
--- /dev/null
+++ b/Src/cellule.hpp
@@ -0,0 +1,10 @@
+#ifndef LIFAP6_LISTE_CELLULE_HPP
+#define LIFAP6_LISTE_CELLULE_HPP
+
+class Cellule {
+
+  /* votre code ici */
+
+} ;
+
+#endif
diff --git a/Src/coordonnees.cpp b/Src/coordonnees.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dd7db0a58ce6955817e2a1305c7ad8059499f6c9
--- /dev/null
+++ b/Src/coordonnees.cpp
@@ -0,0 +1,13 @@
+#include "coordonnees.hpp"
+
+int encode(short int ligne, short int colonne) {
+  return ((int) ligne & 0xffff) | ((int) colonne << 16) ;
+}
+
+short int ligne(int segment) {
+  return segment & 0xffff ;
+}
+
+short int colonne(int segment) {
+  return segment >> 16 ;
+}
diff --git a/Src/coordonnees.hpp b/Src/coordonnees.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c8f625d80917cc676b541469092757169ccb36eb
--- /dev/null
+++ b/Src/coordonnees.hpp
@@ -0,0 +1,9 @@
+#ifndef LIFAP6_LISTES_COORDONNEES_HPP
+#define LIFAP6_LISTES_COORDONNEES_HPP
+
+/* encodage ligne / colonne short int dans un int */
+int encode(short int l, short int c) ;
+short ligne(int segment) ;
+short colonne(int segment) ;
+
+#endif
diff --git a/Src/jeu_serpent.cpp b/Src/jeu_serpent.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..240b329e6fb0f32c70848dccd12b018220dd0611
--- /dev/null
+++ b/Src/jeu_serpent.cpp
@@ -0,0 +1,68 @@
+#include "niveau.hpp"
+#include "serpent.hpp"
+
+#include <iostream>
+#include <ncurses.h>
+#include <unistd.h>
+
+int main() {
+  /* vitesse */
+  float delay = 0.05 ;
+
+  /* elements de jeu */
+  Niveau niveau(40, 40) ;
+  Serpent serpent(20, 20) ;
+
+  /* debut de ncurses */
+  initscr() ;
+  curs_set(0) ;
+  noecho() ;
+  keypad(stdscr, TRUE) ;
+  start_color() ;
+
+  /* dessin initial */
+  niveau.dessiner() ;
+  serpent.dessiner() ;
+  refresh() ;
+
+  /* attente d'une premiere touche */
+  std::cin.peek() ;
+
+  /* plus d'attente sur les carracteres */
+  nodelay(stdscr, TRUE) ;
+
+  /* boucle d'evenements */
+  try {
+    while(1) {
+      /* handle typed keys */
+      usleep(delay * 1E6) ;
+      int c = getch() ;
+      if(c != ERR) {
+        /* arrow keys */
+        if(c == KEY_RIGHT) {
+          serpent.direction = Serpent::DROITE ;
+        } else if (c == KEY_UP) {
+          serpent.direction = Serpent::HAUT ;
+        } else if (c == KEY_LEFT) {
+          serpent.direction = Serpent::GAUCHE ;
+        } else if (c == KEY_DOWN) {
+          serpent.direction = Serpent::BAS ;
+        } else {
+          /* quit if any other key is typed */
+          break ;
+        }
+      }
+      serpent.avancer(niveau) ;
+      serpent.rafraichir() ;
+      refresh() ;
+    }
+
+    /* fin de ncurses */
+    endwin() ;
+  } catch (std::runtime_error &e) {
+    endwin() ;
+    std::cout << "perdu !" << std::endl ;
+  }
+
+  return 0 ;
+}
diff --git a/Src/liste.cpp b/Src/liste.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6cff6d2f7fb018962854f8a6da3aa47fe1737b01
--- /dev/null
+++ b/Src/liste.cpp
@@ -0,0 +1,72 @@
+#include "liste.hpp"
+
+#include <iostream>
+#include <cassert>
+
+Liste::Liste() {
+  /* votre code ici */
+}
+
+Liste::Liste(const Liste& autre) {
+  /* votre code ici */
+}
+
+Liste& Liste::operator=(const Liste& autre) {
+  /* votre code ici */
+  return *this ;
+}
+
+Liste::~Liste() {
+  /* votre code ici */
+}
+
+void Liste::ajouter_en_tete(int valeur) {
+  /* votre code ici */
+}
+
+void Liste::ajouter_en_queue(int valeur) {
+  /* votre code ici */
+}
+
+void Liste::supprimer_en_tete() {
+  /* votre code ici */
+}
+
+Cellule* Liste::tete() {
+  /* votre code ici */
+  return nullptr ;
+}
+
+const Cellule* Liste::tete() const {
+  /* votre code ici */
+  return nullptr ;
+}
+
+Cellule* Liste::queue() {
+  /* votre code ici */
+  return nullptr ;
+}
+
+const Cellule* Liste::queue() const {
+  /* votre code ici */
+  return nullptr ;
+}
+
+int Liste::taille() const {
+  /* votre code ici */
+  return 0 ;
+}
+
+Cellule* Liste::recherche(int valeur) {
+  /* votre code ici */
+  return nullptr ;
+}
+
+const Cellule* Liste::recherche(int valeur) const {
+  /* votre code ici */
+  return nullptr ;
+}
+
+void Liste::afficher() const {
+  /* votre code ici */
+}
diff --git a/Src/liste.hpp b/Src/liste.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c44d58333b460997c7cbd2b55389704e0e422c9e
--- /dev/null
+++ b/Src/liste.hpp
@@ -0,0 +1,55 @@
+#ifndef LIFAP6_LISTE_LISTE_HPP
+#define LIFAP6_LISTE_LISTE_HPP
+
+#include "cellule.hpp"
+
+class Liste {
+
+  public :
+
+    /* construction d'une liste vide */
+    Liste() ;
+
+    /* construction par copie */
+    Liste(const Liste& autre) ;
+
+    /* affectation */
+    Liste& operator=(const Liste& autre) ;
+
+    /* destruction */
+    ~Liste() ;
+
+    /* ajout en tete */
+    void ajouter_en_tete(int valeur) ;
+
+    /* ajout en queue */
+    void ajouter_en_queue(int valeur) ;
+
+    /* suppression en tete */
+    void supprimer_en_tete() ;
+
+    /* consultation de la tete */
+    const Cellule* tete() const ;
+    Cellule* tete() ;
+
+    /* consultation de la queue */
+    const Cellule* queue() const ;
+    Cellule* queue() ;
+
+    /* taille de la liste */
+    int taille() const ;
+
+    /* recherche dans la liste */
+    const Cellule* recherche(int valeur) const ;
+    Cellule* recherche(int valeur) ;
+
+    /* affichage */
+    void afficher() const ;
+
+  private :
+
+    /* votre code ici */
+
+} ;
+
+#endif
diff --git a/Src/niveau.cpp b/Src/niveau.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..04923e78295f4b30bc40c878ecc99f714ac507ab
--- /dev/null
+++ b/Src/niveau.cpp
@@ -0,0 +1,37 @@
+#include "niveau.hpp"
+#include "coordonnees.hpp"
+
+#include <ncurses.h>
+
+Niveau::Niveau(short int hauteur, short int largeur) {
+  /* bords horizontaux */
+  for(int i = 0; i < largeur; ++i) {
+    murs.ajouter_en_tete(encode(0, i)) ;
+    murs.ajouter_en_tete(encode(hauteur - 1, i)) ;
+  }
+
+  /* bords verticaux */
+  for(int i = 0; i < largeur; ++i) {
+    murs.ajouter_en_tete(encode(i, 0)) ;
+    murs.ajouter_en_tete(encode(i, largeur - 1)) ;
+  }
+}
+
+void Niveau::dessiner() {
+  /* couleur bleue */
+  init_pair(1, COLOR_BLUE, COLOR_BLUE) ;
+  attron(COLOR_PAIR(1)) ;
+
+  /* dessin des murs */
+  Cellule* mur = murs.tete() ;
+  while(mur) {
+    short int ml = ligne(mur->valeur) ;
+    short int mc =  colonne(mur->valeur) ;
+    mvaddch(ml, 2*mc, ' ') ;
+    mvaddch(ml, 2*mc + 1, ' ') ;
+    mur = mur->suivante ;
+  }
+
+  /* couleur normale */
+  attroff(COLOR_PAIR(1)) ;
+}
diff --git a/Src/niveau.hpp b/Src/niveau.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d872274cfc732e5e14c0f2e7f19aeb69aa0989b0
--- /dev/null
+++ b/Src/niveau.hpp
@@ -0,0 +1,20 @@
+#ifndef LIFAP6_LISTES_NIVEAU_HPP
+#define LIFAP6_LISTES_NIVEAU_HPP
+
+#include "liste.hpp"
+
+class Niveau {
+
+  public :
+
+    /* construction */
+    Niveau(short int hauteur, short int largeur) ;
+
+    /* murs */
+    Liste murs ;
+
+    /* affichage */
+    void dessiner() ;
+} ;
+
+#endif
diff --git a/Src/serpent.cpp b/Src/serpent.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f78177eedfb653e772c39ed1a3ea366e70b141e6
--- /dev/null
+++ b/Src/serpent.cpp
@@ -0,0 +1,97 @@
+#include "serpent.hpp"
+#include "coordonnees.hpp"
+
+#include <stdexcept>
+#include <cassert>
+#include <ncurses.h>
+
+Serpent::Serpent(short int ligne, short int colonne) {
+  direction = HAUT ;
+  m_etat = GRANDISSANT ;
+  m_jauge = 5 ;
+  m_segments.ajouter_en_queue(encode(ligne, colonne)) ;
+}
+
+void Serpent::avancer(const Niveau& niveau) {
+  /* position de la tete */
+  int tl = ligne(m_segments.queue()->valeur) ;
+  int tc = colonne(m_segments.queue()->valeur) ;
+
+  /* deplacement de la tete dans la bonne direction */
+  switch(direction) {
+    case GAUCHE :
+      --tc ;
+      break ;
+    case HAUT :
+      --tl ;
+      break ;
+    case DROITE :
+      ++tc ;
+      break ;
+    case BAS :
+      ++tl ;
+      break ;
+    default :
+      assert(0 && "direction inconnue") ;
+      break ;
+  }
+
+  /* suppression de la queue si stable */
+  if(m_etat == STABLE) {
+    m_segments.supprimer_en_tete() ;
+  } else {
+    --m_jauge ;
+    if(m_jauge == 0) {
+      m_etat = STABLE ;
+    }
+  }
+
+  /* test de collision */
+  int tete = encode(tl, tc) ;
+  if(m_segments.recherche(tete)) {
+    throw(std::runtime_error("collision")) ;
+  }
+  if(niveau.murs.recherche(tete)) {
+    throw(std::runtime_error("collision")) ;
+  }
+
+  /* ajout de la tete deplacee */
+  m_segments.ajouter_en_queue(tete) ;
+}
+
+void Serpent::dessiner() {
+  /* couleur bleue */
+  init_pair(1, COLOR_BLUE, COLOR_BLUE) ;
+  attron(COLOR_PAIR(1)) ;
+  /* itération sur les segments */
+  Cellule* segment = m_segments.tete() ;
+  while(segment) {
+    short int sl = ligne(segment->valeur) ;
+    short int sc = colonne(segment->valeur) ;
+    mvaddch(sl, 2*sc, ' ') ;
+    mvaddch(sl, 2*sc + 1, ' ') ;
+    segment = segment->suivante ;
+  }
+  /* couleur normale */
+  attroff(COLOR_PAIR(1)) ;
+}
+
+void Serpent::rafraichir() {
+  /* couleur bleue */
+  init_pair(1, COLOR_BLUE, COLOR_BLUE) ;
+  attron(COLOR_PAIR(1)) ;
+  /* dessin de la tete */
+  short int tl = ligne(m_segments.queue()->valeur) ;
+  short int tc = colonne(m_segments.queue()->valeur) ;
+  mvaddch(tl, 2*tc, ' ') ;
+  mvaddch(tl, 2*tc + 1, ' ') ;
+  /* couleur normale */
+  attroff(COLOR_PAIR(1)) ;
+  if(m_etat == STABLE) {
+    /* effacement de la queue */
+    short int ql = ligne(m_segments.tete()->valeur) ;
+    short int qc = colonne(m_segments.tete()->valeur) ;
+    mvaddch(ql, 2*qc, ' ') ;
+    mvaddch(ql, 2*qc + 1, ' ') ;
+  }
+}
diff --git a/Src/serpent.hpp b/Src/serpent.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0d88550e35ffa00c93b6c1c0a4d9e62b048b2eab
--- /dev/null
+++ b/Src/serpent.hpp
@@ -0,0 +1,47 @@
+#ifndef LIFAP6_LISTE_SERPENT_HPP
+#define LIFAP6_LISTE_SERPENT_HPP
+
+#include "liste.hpp"
+#include "niveau.hpp"
+
+class Serpent {
+
+  public :
+
+    /* creation a une position donnee */
+    Serpent(short int ligne, short int colonne) ;
+
+    /* direction courante de deplacement */
+    enum Direction {
+      DROITE,
+      HAUT,
+      GAUCHE,
+      BAS
+    } ;
+
+    Direction direction ;
+
+    /* deplacement */
+    void avancer(const Niveau& niveau) ;
+
+    /* dessin */
+    void dessiner() ;
+    void rafraichir() ;
+
+  private :
+
+    /* etat du serpent */
+    enum Etat {
+      STABLE,
+      GRANDISSANT
+    } ;
+
+    Etat m_etat ;
+    int m_jauge ;
+
+    /* morceaux de serpent */
+    Liste m_segments ;
+
+} ;
+
+#endif
diff --git a/Src/test_liste.cpp b/Src/test_liste.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f783e469a4f18d496e9f50e0574b9fc747087ee4
--- /dev/null
+++ b/Src/test_liste.cpp
@@ -0,0 +1,78 @@
+#include "liste.hpp"
+
+#include <iostream>
+#include <cassert>
+
+int main() {
+
+  /* adaptez ce fichier selon votre structure */
+
+  Liste l1 ;
+  l1.ajouter_en_tete(10) ;
+  l1.ajouter_en_tete(11) ;
+  std::cout << "attendu : [ 11 10 ]" << std::endl ;
+  l1.afficher() ; // [ 11 10 ]
+  assert(l1.tete()->valeur == 11) ;
+  assert(l1.queue()->valeur == 10) ;
+  assert(l1.recherche(11)) ;
+  assert(l1.recherche(10)) ;
+  assert(!l1.recherche(12)) ;
+  assert(l1.taille() == 2) ;
+  
+  Liste l2(l1) ;
+  l2.ajouter_en_tete(20) ;
+  std::cout << "attendu : [ 20 11 10 ]" << std::endl ;
+  l2.afficher() ; // [ 20 11 10 ]
+  assert(l2.tete()->valeur == 20) ;
+  assert(l2.queue()->valeur == 10) ;
+  assert(l2.recherche(20)) ;
+  assert(l2.recherche(11)) ;
+  assert(l2.recherche(10)) ;
+  assert(!l2.recherche(21)) ;
+
+  Liste l3 ;
+  l3.ajouter_en_tete(30) ;
+  l3 = l1 ;
+  std::cout << "attendu : [ 11 10 ]" << std::endl ;
+  l3.afficher() ; // [ 11 10 ]
+  assert(l3.tete()->valeur == 11) ;
+  assert(l3.queue()->valeur == 10) ;
+  assert(l3.recherche(11)) ;
+  assert(l3.recherche(10)) ;
+  assert(!l3.recherche(30)) ;
+
+  l1.supprimer_en_tete() ;
+  l1.afficher() ; // [ 10 ]
+  assert(l1.tete()->valeur == 10) ;
+  assert(l1.queue()->valeur == 10) ;
+  assert(l1.recherche(10)) ;
+  assert(!l1.recherche(11)) ;
+
+  l1.ajouter_en_tete(12) ;
+  std::cout << "attendu : [ 12 10 ]" << std::endl ;
+  l1.afficher() ; // [ 12 10 ]
+  assert(l1.tete()->valeur == 12) ;
+  assert(l1.recherche(10)) ;
+
+  std::cout << "attendu : [ 20 11 10 ]" << std::endl ;
+  l2.afficher() ; // [ 20 11 10 ]
+  assert(l2.tete()->valeur == 20) ;
+  assert(l2.recherche(11)) ;
+  assert(l2.recherche(10)) ;
+
+  std::cout << "attendu : [ 11 10 ]" << std::endl ;
+  l3.afficher() ; // [ 11 10 ]
+  assert(l3.tete()->valeur == 11) ;
+  assert(l3.recherche(10)) ;
+
+  l3.ajouter_en_queue(31) ;
+  std::cout << "attendu : [ 11 10 31 ]" << std::endl ;
+  l3.afficher() ; // [ 11 10 31 ]
+  assert(l3.tete()->valeur == 11) ;
+  assert(l3.queue()->valeur == 31) ;
+  assert(l3.recherche(11)) ;
+  assert(l3.recherche(10)) ;
+  assert(l3.recherche(31)) ;
+
+  return 0 ;
+}