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 ; +}