Tour de Hanoï

De DigiWiki.

  • Créer 9 cylindres + 3 pour les tours et 1 rectangle pour le socle (racine).
  • Placer le script dans le prim racine.

Enjoy!

// Hanoi v1.0 by Bestmomo Lagan
 
// On affecte un nom au tours, en les distinguant avec un index numérique
// On affecte un nom aux disques en les distinguant aussi avec un index numérique
 
// Les disques s'appellent DISQUE0, DISQUE1 ...
// Les tours s'appellent TOUR0, TOUR1 ...
 
// Nom générique
string NOM_TOURS   = "TOUR";
string NOM_DISQUES = "DISQUE";
 
// Détection des primitives
// On doit connaîte les numéros de liaison des tours et des disques
 
// Listes pour mémoriser les numéros de laison
list l_tours;
list l_disques;
 
// Nombre de tours et de disques
integer i_nbr_tours;
integer i_nbr_disques;
 
// Fonction de détermination des numéros de liaisons
set_prims_link() {
 
    // Nombre total de primitives liées
    integer n_prims = llGetNumberOfPrims();
 
    // Index de fin des noms
    integer i_tours_length = llStringLength(NOM_TOURS) - 1;
    integer i_disques_length = llStringLength(NOM_DISQUES) - 1;
 
    // Première passe pour compter le nombre de tours et de disques
    integer i;
    for(i = 2; i <= n_prims; ++i) {
        // Nom de la primitive
        string nom = llList2String(llGetLinkPrimitiveParams(i, [PRIM_NAME]), 0);
        // Tours
        if(llGetSubString(nom, 0, i_tours_length) == NOM_TOURS) ++i_nbr_tours;
        // Disques
        else if(llGetSubString(nom, 0, i_disques_length) == NOM_DISQUES) ++i_nbr_disques;
    }
 
    // Renseignement des listes à la bonne longueur
    integer n = i_nbr_tours; while(n--) l_tours += 0;
    n = i_nbr_disques; while(n--) l_disques += 0;
 
    // Deuxième passe pour trouver les numéros de laison
    for(i = 2; i <= n_prims; ++i) {
        // Nom de la primitive
        string nom = llList2String(llGetLinkPrimitiveParams(i, [PRIM_NAME]), 0);
        // Tours
        if(llGetSubString(nom, 0, i_tours_length) == NOM_TOURS) {
            integer index = (integer)llGetSubString(nom, i_tours_length + 1, -1);
            l_tours = llListReplaceList(l_tours, [i], index, index);
        }
        // Disques
        else if(llGetSubString(nom, 0, i_disques_length) == NOM_DISQUES) {
            integer index = (integer)llGetSubString(nom, i_disques_length + 1, -1);
            l_disques = llListReplaceList(l_disques, [i], index, index);
        }
    }
}
 
// Fonctions pour récupérer un numéro de liaison à partir de son index
integer get_tour_link(integer id_tour) {
    return llList2Integer(l_tours, id_tour);
}
integer get_disque_link(integer id_disque) {
    return llList2Integer(l_disques, id_disque);
}
 
// Fonction pour récupérer un index à partir d'un numéro de liaison
integer get_tour_index(integer tour_link) {
    return llListFindList(l_tours, [tour_link]);
}
 
// Définition des emplacements
// On doit connaître l'emplacement des disques pour chaque tour
 
// Epaisseur d'un disque
float f_disque_height;
set_disque_height() {
    vector size = llList2Vector(llGetLinkPrimitiveParams(get_disque_link(0), [PRIM_SIZE]), 0);
    f_disque_height = size.z;
}
 
// Hauteur d'une tour
float f_tour_height;
set_tour_height() {
    list l = llGetLinkPrimitiveParams(get_tour_link(0), [PRIM_SIZE]);
    vector size = llList2Vector(l, 0);
    f_tour_height = size.z;
}
 
// Emplacement de stockage des disques
vector v_stockage;
set_stockage() {
    list l = llGetLinkPrimitiveParams(get_tour_link(0), [PRIM_POS_LOCAL]);
    v_stockage = llList2Vector(l, 0) - <.0, .0, f_tour_height / 2.0 + f_disque_height + .01>;
}
 
// Emplacement de base des tours
list l_tours_bases;
set_bases() {
    integer n = i_nbr_tours;
    integer i;
    for(; i < i_nbr_tours; ++i) {
        list l = llGetLinkPrimitiveParams(get_tour_link(i), [PRIM_POS_LOCAL]);
        l_tours_bases += llList2Vector(l, 0) - <.0, .0, f_tour_height / 2.0 - f_disque_height / 2.0>;
    }
}
 
// Emplacement avec un index de tour et un index de place
vector get_pos(integer id_tour, integer id_place) {
    vector v_base = llList2Vector(l_tours_bases, id_tour);
    return v_base + <0, 0, id_place * f_disque_height>;
}
 
// Liste de la tour avec son index
list get_tour_list(integer i_tour) {
    if(i_tour == 0) return l_tour1;
    else if(i_tour == 1) return l_tour2;
    else return l_tour3;
}
 
// Référence du disque supérieur (-1 si pas de disque)
integer get_disque_sup(integer i_tour) {
    list l = get_tour_list(i_tour);
    integer n = llGetListLength(l);
    // Index du disque supérieur
    if(n)return llList2Integer(l, n - 1);
    // Pas de disque
    else return -1;
}
 
// Récupération base de la tour
vector get_base(integer i_tour) {
    list l = get_tour_list(i_tour);
    return llList2Vector(l_tours_bases, i_tour);
}
 
// Emplacement du disque supérieur
vector get_disque_sup_place(integer i_tour) {
    list l = get_tour_list(i_tour);
    return get_base(i_tour) + <0, 0, (llGetListLength(l) - 1) * f_disque_height>;
}
 
// Emplacement libre en haut de la tour
vector get_disque_free_place(integer i_tour) {
    list l = get_tour_list(i_tour);
    return get_base(i_tour) + <0, 0, (llGetListLength(l)) * f_disque_height>;
}
 
 
// Emplacement pour déplacement
vector get_disque_move(integer i_tour) {
    return get_base(i_tour) + <0, 0, f_tour_height + f_tour_height / 10.0>;
}
 
 
// Mouvements: Définition des mouvements avec interpolation
 
// Temps de base
float        TEMPS_BASE    = .04;
// Durée de transition
float        TIME          = 1.0;
 
// Interpolation sinusoïdale de deux vecteurs
vector v_cos(vector v1, vector v2, float t) {
    float f = (1.0 - llCos(t * PI)) / 2.0;
    return v1 * (1.0 - f) + v2 * f;
}
 
// Déplacement
set_move(integer id_disque, vector v1, vector v2) {
    // Numéro de liaison
    integer i_link = get_disque_link(id_disque);
    // Durée d'une étape élémentaire
    float f_step = 1.0 / (TIME / TEMPS_BASE);
    // Etapes cumulées
    float step;
    // Boucle du mouvement
    while((step += f_step) <= 1.0) {
        llSetLinkPrimitiveParamsFast(i_link, [PRIM_POSITION, v_cos(v1, v2, step)]);
        llSleep(TEMPS_BASE);
    }
}
 
 
// Gestion du jeu:
 
// Listes d'état d'occupation des tours
list        l_tour1;
list        l_tour2;
list        l_tour3;
 
// Nombre de disques choisis
integer    i_nbr_disques_jeu;
 
// Index de départ des disques
integer    i_index;
 
// Modes (0 = repos, 1 = choix jeu , 2 = choix démo, 3 = jeu en cours , 4 = démo en cours)
integer    i_mode;
 
// Etat du déplacement
integer    i_move = -1;
 
// Reset des disques
reset_disques() {
    // Vidage des tours
    l_tour1 = [];
    l_tour2 = [];
    l_tour3 = [];
    // Stockage des disques
    integer n = i_nbr_disques;
    while(n--) llSetLinkPrimitiveParamsFast(get_disque_link(n), [PRIM_POSITION, v_stockage]);
}
 
// Mise en place initiale
init_disques() {
    reset_disques();
    // Mise en place
    i_index = i_nbr_disques - i_nbr_disques_jeu;
    integer n;
    for(n = i_index; n < i_nbr_disques; ++n) {
        llSetLinkPrimitiveParamsFast(get_disque_link(n), [PRIM_POSITION, get_pos(0, n - i_index)]);
        l_tour1 += n;
    }
}
 
// Test coup correct
integer test_correct(integer i_tour, integer i_disque) {
    return i_disque > get_disque_sup(i_tour);
}
 
// Test de fin de jeu
integer test_fin() {
    return llGetListLength(l_tour3) == i_nbr_disques_jeu;
}
 
// Transfert disque entre tours
transfert_disque(integer i_tour_depart, integer i_tour_arrivee) {
    integer i;
    if(i_tour_depart == 0) i = llList2Integer(l_tour1, -1);
    else if(i_tour_depart == 1) i = llList2Integer(l_tour2, -1);
    else if(i_tour_depart == 2) i = llList2Integer(l_tour3, -1);
    if(i_tour_arrivee == 0) l_tour1 += i;
    else if(i_tour_arrivee == 1) l_tour2 += i;
    else if(i_tour_arrivee == 2) l_tour3 += i;
    if(i_tour_depart == 0) l_tour1 = llDeleteSubList(l_tour1, -1, -1);
    else if(i_tour_depart == 1) l_tour2 = llDeleteSubList(l_tour2, -1, -1);
    else if(i_tour_depart == 2) l_tour3 = llDeleteSubList(l_tour3, -1, -1);
}
 
 
// Gestion des menus:
 
// Canal aléatoire
integer get_channel() {
    return ~(integer)llFrand((float)DEBUG_CHANNEL);
}
 
// Variables pour menu
integer     i_ecoute;
integer     i_canal;
integer     i_inhibe;
float       f_timeout = 30.0;
key         k_ava;
 
// Reset menu
reset_menu() {
    i_inhibe = FALSE;
    llSetTimerEvent(.0);
    llListenRemove(i_ecoute);
}
 
// Mise en action d'un menu
set_menu(key ava, string texte, list menu) {
    i_inhibe = TRUE;
    i_canal = get_channel();
    k_ava = ava;
    i_ecoute = llListen(i_canal, "", k_ava, "");
    llSetTimerEvent(f_timeout);
    llDialog(k_ava, texte, menu, i_canal);
}
 
// Menus
list    MENU1     = ["Quitter","Jeu","Démo"];
list    MENU2     = ["Retour"," "," ","1","2","3","4","5","6","7","8","9"];
list    MENU3     = ["Non","Oui"];
 
// Textes
string  TEXTE1    = "Choisissez une option :";
string  TEXTE2    = "Choisissez le nombre de disques :";
string  TEXTE3    = "Quitter le jeu en cours ?";
 
// Mode démo:
 
// Déplacement
move_disque(integer i_start, integer i_end) {
    integer i_disque = get_disque_sup(i_start);
    // Montée
    set_move(i_disque, get_disque_sup_place(i_start), get_disque_move(i_start));
    // Déplacement latéral
    set_move(i_disque, get_disque_move(i_start), get_disque_move(i_end));
    // Descente
    set_move(i_disque, get_disque_move(i_end), get_disque_free_place(i_end));
    // Mise à jour
    transfert_disque(i_start, i_end);
}
 
// Fonction récursive
hanoi(integer n, integer i_start, integer i_end, integer i_step) {
    if(n == 1) move_disque(i_start, i_end);
    else {
        hanoi(n - 1, i_start, i_step, i_end);
        move_disque(i_start, i_end);
        hanoi(n - 1, i_step, i_end, i_start);
    }
}
 
 
// Etat par défaut:
 
default
{
    // Initialisations au démarrage du script
    state_entry() {
        // Détection des numéros de laison
        set_prims_link();
        // Epaisseur d'un disque
        set_disque_height();
        // Hauteur d'une tour
        set_tour_height();
        // Emplacement de base des tours
        set_bases();
        // Emplacement de stockage des disques
        set_stockage();
    }
 
    touch_start(integer total_number) {
        // Numéro de liaison de la prim touchée
        integer i_link = llDetectedLinkNumber(0);
        // Avatar qui a touché
        key ava = llDetectedKey(0);
        // Cas du root
        if(i_link == 1) {
            if(ava != k_ava) {
                if(i_inhibe) llInstantMessage(ava, "Désolé mais le menu est déjà utilisé...");
                else if(i_mode > 2) llInstantMessage(ava, "Désolé mais un jeu est déjà en cours...");
                else set_menu(ava, TEXTE1, MENU1);
            }
            else {
                if(i_mode) set_menu(ava, TEXTE3, MENU3);
                else set_menu(ava, TEXTE1, MENU1);
            }
        }
 
        // Cas d'une prim enfant
        else {
            // Cas d'une tour
            if(~llListFindList(l_tours, [i_link])) {
                // Index de la tour
                integer i_tour = get_tour_index(i_link);
                // Même tour (retour en position initiale)
                if(i_tour == i_move) {
                    // Index du disque supérieur
                    integer i_disque = get_disque_sup(i_tour);
                    // Mouvement s'il y a un disque
                    if(~i_disque) {
                        i_move = -1;
                        set_move(i_disque, get_disque_move(i_tour), get_disque_sup_place(i_tour));
                    }
                }
                // Autres cas
                else {
                    // Déplacement
                    if(~i_move) {
                        // Index du disque supérieur
                        integer i_disque = get_disque_sup(i_move);
                        // Mouvement si coup correct
                        if(test_correct(i_tour, i_disque)) {
                            // Déplacement latéral
                            set_move(i_disque,  get_disque_move(i_move), get_disque_move(i_tour));
                            // Descente
                            set_move(i_disque,  get_disque_move(i_tour), get_disque_free_place(i_tour));
                            // Mise à jour
                            transfert_disque(i_move, i_tour);
                            i_move = -1;
                            // Test de fin
                            if(test_fin()) {
                                llWhisper(0, "Jeu terminé !");
                                i_mode = 0;
                            }
                        }
                        // Coup incorrect
                        else llWhisper(0, "Vous ne pouvez pas poser ce disque sur cette tour");
                    }
                    // Montée du disque
                    else {
                        // Index du disque supérieur
                        integer i_disque = get_disque_sup(i_tour);
                        // Mouvement s'il y a un disque
                        if(~i_disque) {
                            i_move = i_tour;
                            set_move(i_disque, get_disque_sup_place(i_tour), get_disque_move(i_tour));
                        }
                    }
                }
            }
        }
    }
 
    listen(integer channel, string name, key ava, string message){
        reset_menu();
        // Menu 1
        if(~llListFindList(MENU1, [message])) {
            if(message != "Quitter") {
                if(message == "Démo") i_mode = 2;
                else i_mode = 1;
                set_menu(ava, TEXTE2, MENU2);
            }
        }
        // Menu 2
        else if(~llListFindList(MENU2, [message])) {
            // Touche neutre
            if(message == " ") set_menu(ava, TEXTE2, MENU2);
            // Retour au menu 1
            else if(message == "Retour") set_menu(ava, TEXTE1, MENU1);
            // Nombre de disques choisi, on peut jouer
            else {
                i_nbr_disques_jeu = (integer)message;
                i_mode += 2;
                init_disques();
                if(i_mode == 4) {
                    llSleep(1.0);
                    hanoi(i_nbr_disques_jeu, 0, 2, 1);
                    llWhisper(0, "Démo terminée !");
                    i_mode = 0;
                }
            }
        }
        // Menu 3
        else if(~llListFindList(MENU3, [message])) {
            // Retour au menu 1
            if(message == "Oui") {
                i_mode = 0;
                set_menu(ava, TEXTE1, MENU1);
            }
        }
    }
 
    timer() {
        reset_menu();
        llInstantMessage(k_ava, "Délai écoulé, désolé...");
    }
}
Outils personnels
  • Cette page a été consultée 715 fois.
donate
Google Ads