Code source d'Icosien

Licence

Ce code est fourni sous une licence CC-By (cf. fin de page).
De plus, il est interdit de recompiler le jeu "tel quel" en supprimant le lien « Sponsorisé par », la mention « tel quel » restant à l'appréciation de l'auteur original du code source (copie, plagiat...).

Télécharger tous les codes sources.

Package principal

Fichiers de bases :

Code source : Preloader.as
?package {
import com.greensock.TweenLite;
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.display.Shape;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.utils.getDefinitionByName;
import mochi.as3.*;
// Must be dynamic!
public dynamic class Preloader extends MovieClip {
// Keep track to see if an ad loaded or not
private var did_load:Boolean;
// Change this class name to your main class
public static var MAIN_CLASS:String = "Battery";

private var Cercle:Shape = new Shape();

public function Preloader()
{
super();
var f:Function = function(ev:IOErrorEvent):void {
// Ignore event to prevent unhandled error exception
}
loaderInfo.addEventListener(IOErrorEvent.IO_ERROR, f);

var opts:Object = {
clip:this,
id:"803e3dbb622e59fc",
res:"640x480",
background:0xFFFFC9,
color:0xFF8A00,
outline:0xD58B3C,
no_bg:true
//ad_finished:startup
};

opts.ad_started = function ():void {
did_load = true;
}

opts.ad_finished = function ():void {
// don't directly reference the class, otherwise it will be
// loaded before the preloader can begin
loaderInfo.removeEventListener(ProgressEvent.PROGRESS, progress);

if(contains(Cercle))
removeChild(Cercle);
Cercle.graphics.clear();
var mainClass:Class = Class(getDefinitionByName("Main"));
var app:Object = new mainClass();
addChild(app as DisplayObject);
}

opts.ad_skipped = opts.ad_finished;

loaderInfo.addEventListener(ProgressEvent.PROGRESS, progress);
MochiAd.showPreGameAd(opts);
//Main.HEIGHT;
//opts.ad_finished()


Cercle.graphics.beginFill(0xba984f);
//Cercle.graphics.lineStyle(2);
Cercle.x = 320; Cercle.y = 240;
Cercle.graphics.drawCircle(0, 0, 1);
addChild(Cercle);
setChildIndex(Cercle, 0);
}

private function progress(e:ProgressEvent):void
{
TweenLite.to(Cercle, .5, { scaleX:400 * e.bytesLoaded / e.bytesTotal, scaleY:400 * e.bytesLoaded / e.bytesTotal } );
}
}
}
Code source : Main.as
?package 
{
import com.greensock.TweenLite;
import flash.display.Bitmap;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.ContextMenuEvent;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.filters.BevelFilter;
import flash.filters.DropShadowFilter;
import flash.net.sendToURL;
import flash.net.SharedObject;
import flash.net.URLRequest;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import flash.utils.getTimer;
import mochi.as3.MochiAd;
import Levels.*;

/**
* Icosien : le jeu flash :)
* Exploite les idées de graphe Eulérien et Hamiltonien sous forme de jeu.
* http://neamar.fr/Res/Icosien
* @author Neamar
*/
public class Main extends Sprite
{
/**
* Taille du SWF.
*/
public static const WIDTH:int = 640;
public static const HEIGHT:int = 480;
public static const SLIDE_DUREE:Number = 2;

/**
* L'ombre, c'est la même pour tout le monde.
* On la définit une bonne fois pour toute ici pour économiser la mémoire.
*/
public static const Ombre:Array = new Array(new DropShadowFilter(8,45,0,.8));

/**
* L'image des plantes en haut.
*/
[Embed(source = "../assets/Plante.png")]
private static var Plant:Class;
private var Plante:Bitmap = new Plant();

/**
* Les données définissant le niveau.
*/
public const Datas:Vector.<LevelDatas> = new Vector.<LevelDatas>();

/**
* Le numéro du niveau actuel.
* 0 pour commencer, on peut le modifier pour les tests.
*/
private var _NumeroNiveauActuel:int;

/**
* L'objet permettant de retenir à quel niveau on en est.
*/
private var Partage:Object = new Object();

/**
* L'objet contenant le niveau actuel, sur lequel on a enregistré l'evenement Level.LEVEL_WIN
*/
private var NiveauActuel:Level=null;

/**
* Le dernier niveau joué, stocké temporairement pour destruction une fois sorti de l'écran.
*/
private var AncienNiveau:Level;

/**
* La planche au fond.
*/
private var Fond:Background = new Background();

/**
* Mdofications du menu contextuel
*/
private var menuItemRestart:ContextMenuItem = new ContextMenuItem("Recommencer le niveau");
private var menuItemPrevious:ContextMenuItem = new ContextMenuItem("Reculer d'un niveau");
private var menuItemNext:ContextMenuItem = new ContextMenuItem("Avancer d'un niveau");

private var myMenu:ContextMenu = new ContextMenu();

/**
* Gestion de la pub.
*/
private var Pub:MovieClip = new MovieClip();
private var DernierePub:int = getTimer();//La première pub arrive au bout de 15 minutes de jeu (5 + 10=.
private const PUB_INTERVAL:int = 1000 * 60 * 10;//Puis toutes les dix minutes.
private var IsShowingAd:Boolean = false;//Empêche de passer les publicités.

public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}

/**
* Initialiser l'application et construire la banque de données pour les niveaux.
*/
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);

addChild(Pub);
Pub.visible = false;
Pub.stageParent = this;

//Enregistrer le nouveau joueur de façon asynchrone :
sendToURL(new URLRequest("http://neamar.fr/Res/Icosien/Player.php"));

//Créer l'objet de sauvegarde locale :
var LocalValue:Object = SharedObject.getLocal("Icosien").data;

//Réinitialiser les valeurs enregistrées (debug)
//Commenter cette ligne pour avoir un enregistrement de la progression.
//LocalValue.NumeroNiveauActuel = null;


//Et récupérer le dernier niveau (s'il existe)

if (LocalValue.NumeroNiveauActuel == null)
{
trace("(Re-)set");
LocalValue.NumeroNiveauActuel = 0;
}
else
{
NumeroNiveauActuel = LocalValue.NumeroNiveauActuel;
}


//Charger la banque de donnée des niveaux.
include 'DatasBank.as';

addChild(Fond);
addChild(Plante);
Plante.filters = Ombre;
getNextLevel();

//Permettre la navigation au clavier.
function moveKeyboard(e:KeyboardEvent):void
{
//CHEAT ! Ctrl + Alt + Flèche droite permet le déplacement direct.
if (e.ctrlKey && e.altKey && e.keyCode == 39 && NumeroNiveauActuel<Datas.length)
moveLevel(1);
else if (e.keyCode == 39)//Flèche droite : charger un niveau si disponible.
getNextLevel();
else if (e.keyCode == 37)
getPreviousLevel();
else if (e.keyCode == 27 || e.keyCode == 82 || e.keyCode == 75 || e.keyCode == 32)
getSameLevel();
}
stage.addEventListener(KeyboardEvent.KEY_UP, moveKeyboard);


//Configurer le menu clic droit
menuItemRestart.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,getSameLevel);
menuItemPrevious.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,getPreviousLevel);
menuItemNext.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,getNextLevel);
myMenu.customItems.push(menuItemRestart);
myMenu.customItems.push(menuItemNext);
myMenu.customItems.push(menuItemPrevious);

myMenu.builtInItems.play = myMenu.builtInItems.loop = myMenu.builtInItems.rewind = myMenu.builtInItems.forwardAndBack = myMenu.builtInItems.print = false;

contextMenu = myMenu;
}

/**
* Relance le niveau actuel.
* @param e Un évenement non utilisé.
*/
public function getSameLevel(e:Event = null):void
{
if (NiveauActuel != null)
NiveauActuel.restart();
}

/**
* Passer au niveau suivant si on a déjà débloqué le niveau
* @param e Un évenement non utilisé.
*/
public function getNextLevel(e:Event = null):void
{
if((e!=null && e.type==Level.LEVEL_WIN) || canGetNextLevel())
moveLevel(1);
}

/**
* Passer au niveau précédent si on n'est pas au niveau 0.
* @param e Un évenement non utilisé.
*/
public function getPreviousLevel(e:Event = null):void
{
if(canGetPreviousLevel())
moveLevel(-1);
}

/**
* Changer le niveau en cours pour le niveau suivant / précédent.
* @param Sens S'il faut ajouter un niveau ou en retirer un.
*/
private function moveLevel(Sens:int):void
{
//Vérifier la validité de l'appel de fonction
if (Math.abs(Sens) > 1)
throw new Error("Impossible de se déplacer de plus d'un niveau à la fois.");

//Ne pas sortir par la gauche !
if (NumeroNiveauActuel == 0 && Sens == -1)
return;

if (NumeroNiveauActuel == Datas.length && Sens == 1)
Sens = 0;

//Allez les vieux, faites de la place aux jeunes.
AncienNiveau = NiveauActuel;
if(AncienNiveau != null)
{
AncienNiveau.removeEventListener(Level.LEVEL_WIN, getNextLevel);
if(AncienNiveau.Toile!=null)
AncienNiveau.Toile.disconnect();
}

//Changer le niveau, ou afficher une pub.
if (DernierePub + PUB_INTERVAL < getTimer())
{//Afficher une pub.
NiveauActuel = new AdLevel("", Pub);
(NiveauActuel as TextLevel).Caption = "Votre jeu va reprendre dans quelques secondes...";
DernierePub = getTimer() + 10000;
IsShowingAd = true;
}
else
{
IsShowingAd = false;

//Passer au niveau suivant
NumeroNiveauActuel += Sens;
//Charger le niveau de la banque de données.
NiveauActuel = Datas[NumeroNiveauActuel - 1].build();

//Afficher la progression :
if (NiveauActuel is TextLevel && NumeroNiveauActuel!=1 && NumeroNiveauActuel!=Datas.length)
(NiveauActuel as TextLevel).Caption = (NumeroNiveauActuel-1) + " / " + (Datas.length-2);//-1 car la présentation ne compte pas, -2 car le début et la fin ne comptent pas.

//Si on arrive à l'avant dernier niveau, donner l'ordre de précharger l'image finale :)
if (NumeroNiveauActuel == Datas.length - 1)
EndLevel.downloadDatas();
}

addChild(NiveauActuel);

//Modifier le menu contextuel et les suivants / précédents
menuItemPrevious.enabled = canGetPreviousLevel();
menuItemNext.enabled = canGetNextLevel();

//(re-)Passer la plante au premier plan et la planche au dernier.
setChildIndex(Plante, numChildren - 1);
setChildIndex(Fond, 0);

//Faire défiler la planche à l'arrière d'un cran, et bouger les niveaux.
//La variable Sens détermine la direction des animations (à droite / à gauche)
NiveauActuel.x = Sens * Main.WIDTH;
TweenLite.to(Fond, SLIDE_DUREE, { x:Fond.x - Sens*Main.WIDTH } );
if (AncienNiveau != null)
TweenLite.to(AncienNiveau, SLIDE_DUREE, { x: - Sens*Main.WIDTH, onComplete:removeAfterMove } );
TweenLite.to(NiveauActuel, SLIDE_DUREE, { x:0 } );

NiveauActuel.addEventListener(Level.LEVEL_WIN, getNextLevel);
}

/**
* Déclenché une fois le niveau fini, cette fonction est déclenchée pour mettre à jour le niveau actuel.
* Cette fonction n'est pas appelée au tout début.
*/
private function removeAfterMove():void
{
if (AncienNiveau != null && contains(AncienNiveau))
{
var i:int = 0;
while (!(getChildAt(i) is Level)) { i++ };

//Supprimer le premier élément sur la scène, qui est logiquement le plus ancien.
removeChild(getChildAt(i));
}

// AncienNiveau.destroy(); => implicite avec le removeChild.
}

/**
* Numéro actuel du niveau en jeu.
*/
public function get NumeroNiveauActuel():int { return _NumeroNiveauActuel; }

/**
* Met à jour le niveau actuel, et enregistre sur le disque la progression.
*/
public function set NumeroNiveauActuel(value:int):void
{

try
{
//Enregistrer le plus haut niveau atteint sur le disque (si on en a la permission)
SharedObject.getLocal("Icosien").data.NumeroNiveauActuel = Math.max
(
SharedObject.getLocal("Icosien").data.NumeroNiveauActuel,
value-1
);
//écrire les données sur le disque.
SharedObject.getLocal("Icosien").flush();
}
catch (e:Error)
{
trace("Accès au disque interdit :(, impossible d'enregistrer le niveau atteint.");
}
finally
{
_NumeroNiveauActuel = value;
}
}

private function canGetNextLevel():Boolean { return (NumeroNiveauActuel <= SharedObject.getLocal("Icosien").data.NumeroNiveauActuel && !IsShowingAd); }
private function canGetPreviousLevel():Boolean { return (NumeroNiveauActuel > 1 && !IsShowingAd); }
}
}
import Levels.Level;

/**
* Classe "database" pour stocker les niveaux à réaliser dans une structure légère.
*/
class LevelDatas
{
public var Type:Class;
public var Datas:String;
public var Name:String;
public var AdditionalDatas:*;


private var Niveau:Level;

/**
* Crée un nouvel Objet LevelDatas.
* @param Type le type du niveau (EulerLevel ou HamiltonLevel)
* @param Datas Les données du niveau au format graphe
* @param Name Le nom du niveau (non affiché)
* @param AdditionalDatas Second paramètre du constructeur de Type -- si nécessaire (ex: DummyLevel).

*/
public function LevelDatas(Type:Class, Datas:String, Name:String, AdditionalDatas:*=null)
{
this.Type = Type;
this.Datas = Datas;
this.Name = Name;
this.AdditionalDatas = AdditionalDatas;
}

/**
* Construit le niveau demandé et le renvoie.
*/
public function build():Level
{
//Construire l'objet
if(AdditionalDatas==null)
Niveau = new Type(Datas);
else
Niveau = new Type(Datas, AdditionalDatas);

//Et le renvoyer
return Niveau;
}
}
Code source : DatasBank.as
?/**
* INTRODUCTION DU JEU : TITRE DU JEU
*/

Datas.push(new LevelDatas(
CreditsLevel,
"",
"Made by <a href=\"http://neamar.fr\">Neamar</a>, designed by <a href=\"http://licoti2.free.fr\">Licoti</a>",
"Sponsorisé par http://www.mini-jeu-gratuit.fr"));

/**
* PREMIÈRE PARTIE DE JEU : EULER
*/
Datas.push(new LevelDatas(
EulerPathLevel,
"530,310|440,355|540,355|590,380|440,405|540,405|530,450:0,2|2,1|1,4|4,5|5,6|6,3|3,0",
"Flèche tutoriel 1",
"<p>Reproduisez le motif gris d'un seul mouvement de souris, sans repasser deux fois sur le même trait (mais les croisements de fil sont autorisés).<br /> Double cliquez pour recommencer !</p><p></p>Ayez des mouvements amples de souris, imaginez que vous tirez un fil derrière vous : pas besoin de frôler les clous !</p><p></p><p>Pour commencer à jouer, entraînez-vous sur la flèche ci-dessous.<br />Cliquez sur un clou pour attacher votre corde et commencer.</p>"
));

Datas.push(new LevelDatas(
EulerPathLevel,
"320,50|200,150|440,150|200,400|440,400:0,1|0,2|1,2|1,3|1,4|2,3|2,4|3,4",
"Maison"));

Datas.push(new LevelDatas(
EulerPathLevel,
"290,50|350,50|220,150|420,150|320,300|320,400|320,440:0,1|0,2|0,3|0,4|1,2|1,3|1,4|2,4|2,5|3,4|3,5|5,6",
"Labelled Eulergraph.svg http://en.wikipedia.org/wiki/File:Labelled_Eulergraph.svg"));

Datas.push(new LevelDatas(
EulerPathLevel,
"172,92|172,388|468,388|468,92|320,110|190,240|320,370|450,240|285,205|285,275|355,275|355,205:0,1|0,3|0,4|0,5|1,2|1,5|1,6|2,3|2,6|2,7|3,4|3,7|4,8|4,11|5,8|5,9|6,9|6,10|7,10|7,11|8,9|9,10|10,11|11,8",
"Cuboctahedral Graph http://mathworld.wolfram.com/CuboctahedralGraph.html"));

Datas.push(new LevelDatas(
EulerPathLevel,
"320,50|270,90|370,90|320,130|150,250|490,250|100,290|220,280|420,280|540,290|150,330|490,330:0,1|0,2|1,3|1,2|2,3|1,4|2,5|4,6|4,7|4,10|5,8|5,9|5,11|6,10|7,10|8,11|9,11|10,11|7,8",
"Graphe d'explication algorithme de Fleury http://www.cmis.brighton.ac.uk/~jt40/MM322/MM322_FleurysAlgorithm.pdf"));

Datas.push(new LevelDatas(
EulerPathLevel,
"389,74|251,74|154,171|154,309|251,406|389,406|486,309|486,171:0,1|0,2|0,3|0,4|0,6|0,7|1,2|1,3|1,5|1,6|1,7|2,4|2,5|2,6|2,7|3,4|3,5|3,6|3,7|4,5|4,6|4,7|5,6|5,7",
"Cocktail Party Graph http://mathworld.wolfram.com/CocktailPartyGraph.html"));

Datas.push(new LevelDatas(
EulerPathLevel,
"202,78|130,178|130,302|202,402|320,440|438,402|510,302|510,178|438,78|320,40|249,217|276,301|364,301|391,217|320,165:8,9|9,0|0,1|1,2|2,3|3,4|4,5|5,6|6,7|7,8|0,2|2,4|4,6|6,8|8,0|10,12|12,14|14,11|11,13|13,10|9,10|9,13|1,14|1,11|3,10|3,12|5,11|5,13|7,12|7,14",
"Des étoiles plein la tête !"));

Datas.push(new LevelDatas(
EulerPathLevel,
"115,35|115,445|525,445|525,35|387,78|253,78|158,173|158,307|253,402|387,402|482,307|482,173|353,160|287,160|240,207|240,273|287,320|353,320|400,273|400,207|302,222|302,258|338,258|338,222:0,1|1,2|2,3|3,0|0,5|0,6|1,7|1,8|2,9|2,10|3,11|3,4|4,5|5,6|6,7|7,8|8,9|9,10|10,11|11,4|12,13|13,14|14,15|15,16|16,17|17,18|18,19|19,12|20,21|21,22|22,23|23,20|11,19|4,12|5,13|6,14|7,15|8,16|9,17|10,18|19,23|12,23|13,20|14,20|15,21|16,21|17,22|18,22",
"Small Rhombicuboctahedral Graph http://mathworld.wolfram.com/SmallRhombicuboctahedralGraph.html"));

//Datas.push(new LevelDatas(
//EulerPathLevel,
//"451,76|273,35|131,149|131,331|273,445|451,404|530,240|414,123|287,94|185,175|185,305|287,386|414,357|470,240|370,177|302,162|248,205|248,275|302,318|370,303|400,240:0,2|0,5|0,7|0,13|1,3|1,8|1,7|1,6|2,4|2,8|2,9|3,5|3,9|3,10|4,6|4,10|4,11|5,11|5,12|6,13|6,12|7,20|7,15|8,14|8,16|9,15|9,17|10,16|10,18|11,17|11,19|12,18|12,20|13,19|13,14",
//"Brinkmann Graph http://mathworld.wolfram.com/BrinkmannGraph.html"));

Datas.push(new LevelDatas(
EulerPathLevel,
"473,111|355,43|220,67|132,172|132,308|220,413|355,437|473,369|520,240|412,163|341,122|260,136|207,199|207,281|260,344|341,358|412,317|440,240|366,201|330,181|290,188|264,219|264,261|290,292|330,299|366,279|380,240:0,1|1,2|2,3|3,4|4,5|5,6|6,7|7,8|8,0|0,10|0,19|1,11|1,20|2,12|2,21|3,13|3,22|4,14|4,23|5,15|5,24|6,16|6,25|7,17|7,26|8,9|8,18|17,10|10,12|12,14|14,16|16,9|9,11|11,13|13,15|15,17|18,23|23,19|19,24|24,20|20,25|25,21|21,26|26,22|22,18|10,19|11,20|12,21|13,22|14,23|15,24|16,25|17,26",
"Doyle Graph http://mathworld.wolfram.com/DoyleGraph.html"));



/*Datas.push(new LevelDatas(
EulerPathLevel,
"320,50|270,100|370,100|220,150|320,150|420,150|170,200|270,200|370,200|470,200|120,250|220,250|320,250|420,250|520,250|270,300|370,300|220,350|320,350|420,350:0,1|0,2|1,2|1,3|1,4|2,4|2,5|3,6|3,7|3,4|4,5|5,8|5,9|6,10|6,11|6,7|7,11|7,12|8,9|8,12|8,13|9,13|9,14|10,11|11,12|12,13|13,14|12,15|12,16|15,17|15,16|15,18|16,18|16,19|17,18|18,19",
"Sierpi?ski Graph http://mathworld.wolfram.com/SierpinskiGraph.html"));*/

























/**
* DEUXIÈME PARTIE DE JEU : HAMILTON
*/

Datas.push(new LevelDatas(
HamiltonLevel,
"550,340|530,360|450,365|600,380|450,415|600,400|530,420|550,440:0,3|3,2|2,4|2,1|1,6|6,4|4,5|5,7|7,0|3,5",
"Flèche Tutorial Hamilton",
"<p>Vous avez maintenant fini l'échauffement, passons à la partie intéressante !</p><p></p><p>Changement de règle : il faut passer une et une seule fois sur chaque clou, en utilisant uniquement les traits disponibles pour joindre deux clous (mais vous n'êtes pas obligés de passer sur tous les traits).</p><p>Contrainte supplémentaire : il faut commencer et finir sur le même point.</p>"));

Datas.push(new LevelDatas(
HamiltonLevel,
"320,40|111,172|191,418|449,418|529,172|280,240|360,240:0,1|1,2|2,3|3,4|4,0|0,5|0,6|1,6|2,6|3,5|4,5",
"Moser Spindle http://mathworld.wolfram.com/MoserSpindle.html"));

Datas.push(new LevelDatas(
HamiltonLevel,
"420,67|220,67|120,240|220,413|420,413|520,240|370,153|270,153|220,240|270,327|370,327|420,240:0,1|1,2|2,3|3,4|4,5|5,0|6,7|7,8|8,9|9,10|10,11|11,6|0,11|5,6|1,8|2,7|3,10|4,9",
"Franklin Graph http://mathworld.wolfram.com/FranklinGraph.html"));

Datas.push(new LevelDatas(
HamiltonLevel,
"388,31|142,111|142,369|388,449|540,240|366,97|199,152|199,328|366,383|470,240|289,145|220,240|289,335|401,299|401,181|305,192|270,240|305,288|360,269|360,211:0,1|1,2|2,3|3,4|4,0|0,5|1,6|2,7|3,8|4,9|9,13|9,14|5,14|5,10|6,10|6,11|7,11|7,12|8,12|8,13|14,19|10,15|11,16|12,17|13,18|19,15|15,16|16,17|17,18|18,19",
"Dodecahedron http://mathworld.wolfram.com/IcosianGame.html"));

Datas.push(new LevelDatas(
HamiltonLevel,
"258,50|120,240|258,430|482,358|482,122|289,145|220,240|289,335|401,299|401,181|320,240:0,1|1,2|2,3|3,4|4,0|0,6|0,9|1,5|1,7|2,6|2,8|3,7|3,9|4,5|4,8|5,10|6,10|7,10|8,10|9,10",
"Grötzsch graph http://mathworld.wolfram.com/GroetzschGraph.html"));

Datas.push(new LevelDatas(
HamiltonLevel,
"484,167|440,106|376,69|301,61|174,134|144,203|144,277|174,346|230,396|301,419|376,411|440,374|484,313|500,240|320,240:0,1|1,2|2,3|4,5|5,6|6,7|7,8|8,9|9,10|10,11|11,12|12,13|13,0|3,6|5,9|8,12|11,1|13,14|2,14|4,14|7,14|10,14",
"Hypohamiltonian graph constructed by Lindgren http://en.wikipedia.org/wiki/Hypohamiltonian_graph"));

Datas.push(new LevelDatas(
HamiltonLevel,
"476,150|436,102|382,71|258,71|204,102|164,150|143,209|143,271|164,330|204,378|258,409|320,420|382,409|436,378|476,330|497,271|497,209|320,240:0,1|1,2|3,4|4,5|5,6|6,7|7,8|8,9|9,10|10,11|11,12|12,13|13,14|14,15|15,16|16,0|0,17|5,17|2,6|4,9|7,11|8,13|10,14|12,1|3,16",
"First Blanusa Snarks http://mathworld.wolfram.com/BlanusaSnarks.html"));

Datas.push(new LevelDatas(
HamiltonLevel,
"320,20|129,130|129,350|320,460|511,350|511,130|320,90|190,165|190,315|320,390|450,315|450,165|320,150|242,195|242,285|320,330|398,285|398,195:0,1|1,2|2,3|3,4|4,5|5,0|0,6|1,7|2,8|3,9|4,10|5,11|12,15|13,16|14,17|6,13|13,8|8,15|15,10|10,17|17,6|11,12|12,7|7,14|14,9|9,16|16,11",
"Pappus Graph http://mathworld.wolfram.com/PappusGraph.html"));

Datas.push(new LevelDatas(
HamiltonLevel,
"476,84|320,20|164,84|100,240|164,396|320,460|476,396|540,240|414,99|287,73|179,146|153,273|226,381|353,407|461,334|487,207|366,129|274,129|209,194|209,286|274,351|366,351|431,286|431,194:0,1|1,2|2,3|3,4|4,5|5,6|6,7|7,0|0,8|1,9|2,10|3,11|4,12|5,13|6,14|7,15|8,16|9,17|10,18|11,19|12,20|13,21|14,22|15,23|16,19|19,22|22,17|17,20|20,23|23,18|18,21|21,16",
"McGee graph modifié http://en.wikipedia.org/wiki/McGee_graph"));

Datas.push(new LevelDatas(
HamiltonLevel,
"225,30|415,30|255,45|385,45|215,60|425,60|295,75|345,75|320,55|295,95|345,95|215,115|425,115|260,130|380,130|225,145|415,145|240,170|400,170|225,200|415,200|174,448|79,284|172,415|107,302|204,442|99,260|177,365|152,322|148,353|195,355|170,312|252,414|147,233|243,368|183,264|273,391|178,226|287,365|207,227|321,363|561,284|466,448|533,302|468,415|541,260|436,442|488,322|463,365|492,354|470,312|445,355|493,233|388,414|457,264|397,368|462,226|367,391|433,227:0,1|0,2|0,4|1,3|1,5|2,6|2,13|3,7|3,14|4,9|4,11|5,10|5,12|6,8|6,11|7,8|7,12|8,9|8,10|9,13|10,14|11,15|12,16|13,15|14,16|15,17|16,18|17,19|18,20|15,16|17,18|19,20|21,22|21,23|21,25|22,24|22,26|23,27|23,34|24,28|24,35|25,30|25,32|26,31|26,33|27,29|27,32|28,29|28,33|29,30|29,31|30,34|31,35|32,36|33,37|34,36|35,37|36,38|37,39|38,40|39,19|36,37|38,39|19,40|41,42|41,43|41,45|42,44|42,46|43,47|43,54|44,48|44,55|45,50|45,52|46,51|46,53|47,49|47,52|48,49|48,53|49,50|49,51|50,54|51,55|52,56|53,57|54,56|55,57|56,58|56,57|58,20|20,40|21,42|22,0|1,41",
"60 Graph Thomassen"));

Datas.push(new LevelDatas(
HamiltonLevel,
"536,197|523,156|503,118|476,84|442,57|404,37|363,24|320,20|277,24|236,37|198,57|164,84|137,118|117,156|104,197|100,240|104,283|117,324|137,362|164,396|198,423|236,443|277,456|320,460|363,456|404,443|442,423|476,396|503,362|523,324|536,283|540,240:2,0|4,3|5,2|6,1|7,0|8,6|9,5|10,4|11,3|12,2|12,11|13,1|13,10|14,0|14,9|15,8|16,7|17,6|18,5|18,16|19,4|19,15|20,3|20,14|21,2|21,13|22,1|22,12|23,0|23,11|24,10|24,23|25,9|25,22|26,8|26,21|27,7|27,20|28,6|28,19|29,5|29,18|30,4|30,17|31,3|31,16",
"Tauraso's graph http://www.dharwadker.org/hamilton/"));

Datas.push(new LevelDatas(
EndLevel,
"",
":)"));

//Doily http://home.wlu.edu/~mcraea/Finite_Geometry/Applications/Prob33Walks/doily.gif
//NON EULERIEN / NON HAMILTONIEN
//addChild(new EulerPathLevel("382,50|158,122|158,358|382,430|520,240|267,78|150,240|267,402|458,340|458,140|283,126|200,240|283,354|417,311|417,169:4,9|9,0|0,5|5,1|1,6|6,2|2,7|7,3|3,8|8,4|0,7|5,3|1,8|6,4|2,9|5,11|5,14|6,10|6,12|7,11|7,13|8,12|8,14|9,10|9,13"));

Fichiers annexes du package principal

Code source : Background.as
?package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import flash.display.Sprite;
import flash.filters.DisplacementMapFilter;
import flash.geom.Matrix;
/**
* La texture de fond qui défile. Elle est générée à la volée par un sprite.
* Singleton récpérable par Background.Instance() depuis n'importe quel fichier.
*/
public class Background extends Sprite
{

/**
* Accès à l'objet Background de l'application.
*/

public static var Instance:Background;
//Inclut la ressource spécifiée par source, une image png ici.
//De nombreux formats sont gérés : polices, svg, images bitmap, ressources flash...
//Le chemin doit être spécifié par rapport au fichier source.
[Embed(source = "../assets/Plank.png")]
//Définit la classe à instancier pour récupérer un objet de ce type.
private var Plank:Class;

//Instancie un objet de type Plank et le stocke dans une variable de type Bitmap (polymorphisme). Ici, on a bien un type Bitmap (Plank extends Bitmap).
//De toute façon, on ne peut pas mettre :Plank car c'est un type "inconnu à la compilation".
public var FondNormal:Bitmap = new Plank();
public var FondMiroir:Bitmap;

/**
* Le fond qui défile et qui contient la planche et son miroir.
*/
private var FondSeemless:Sprite = new Sprite();

public const Deplacement:Array = new Array(new DisplacementMapFilter(FondNormal.bitmapData, null, BitmapDataChannel.RED, BitmapDataChannel.RED, 5, 5, "color"));

public function Background()
{
Instance = this;

//L'objet s'utilise normalement après.
FondNormal.x = 0;
addChild(FondNormal);

//Construction du miroir
FondMiroir = mirror(FondNormal);
FondMiroir.x = Main.WIDTH;
addChild( FondMiroir );
FondMiroir
}

public override function set x(v:Number):void
{
super.x = v;

//Gérer le décalage des textures : trop à gauche.
if (FondNormal.x + v < -Main.WIDTH)
FondNormal.x += 2 * Main.WIDTH;

if (FondMiroir.x + v < -Main.WIDTH)
FondMiroir.x += 2*Main.WIDTH;

//Gérer le décalage des textures : trop à droite.
if (FondNormal.x + v >= Main.WIDTH)
FondNormal.x -= 2 * Main.WIDTH;

if (FondMiroir.x + v >= Main.WIDTH)
FondMiroir.x -= 2*Main.WIDTH;
}

/**
* Retourne l'image passée en paramètre pour lui appliquer un effet miroir
* @param ImageOriginale l'image à retourner verticalement (les coordonnées Y restent im
* @return
*/
public function mirror(ImageOriginale:Bitmap):Bitmap
{
var ImageMiroir:Bitmap = new Bitmap();

//Construction de la matrice de transformation.
//scaleX = -1
//Translation X :
var Miroir:Matrix = new Matrix( -1, 0, 0, 1, ImageOriginale.width, 0 );

//Construction des données, en dessinant ImageOriginale avec la matrice
var MiroirData:BitmapData = new BitmapData(ImageOriginale.width,ImageOriginale.height);
MiroirData.draw(ImageOriginale, Miroir);

ImageMiroir.bitmapData = MiroirData;
return ImageMiroir;
}

}
}
Code source : Rope.as
?package 
{
import flash.display.Shape;
import flash.filters.BevelFilter;
import flash.geom.ColorTransform;

/**
* Dessine une corde.
* @author Neamar
*/
public class Rope extends Shape
{
private static const Biseau:Array = new Array(new BevelFilter(2));
public const OK:int = 0xca913d;
public const Erreur:int = 0xbd0000;

public var Etat:int = OK;

public function Rope()
{
this.filters = Biseau;
}

public function clear():void
{
graphics.clear();
}

public function moveTo(P:Point):void
{
graphics.moveTo(P.x, P.y);
}

public function lineTo(P:Point):void
{
graphics.lineStyle(3, Etat,.8);
graphics.lineTo(P.x, P.y);
}

}

}
Code source : CustomEvent.as
?package 
{
import flash.events.Event;

/**
* Un évenement personnalisé qui peut prendre des valeurs dynamiques.
* @author Neamar
*/
public dynamic class CustomEvent extends Event
{
public function CustomEvent(Type:String)
{
super(Type);
}
}

}
Code source : Point.as
?package 
{
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;

/**
* Les points composant le graphe.
* @author Neamar
*/
public class Point extends Sprite
{
[Embed(source = "../assets/Clou.png")]
private static var Clou:Class;

[Embed(source = "../assets/Vis.png")]
private static var Vis:Class;

public var Num:int = 0;

/**
* Construit un nouveau point avec les coordonnées spécifiées.
* @param x
* @param y
*/
public function Point(x:Number, y:Number, Num:int=0 )
{
//function T(e:Event):void { trace(Num+1) }
//addEventListener(MouseEvent.MOUSE_MOVE, T) ;

this.Num = Num;


this.x = x;
this.y = y;

//Générer l'image
toNail();
}

/**
* Transformer le point en clou
*/
public function toNail():void
{
toItem(Clou);
}

/**
* Transformer le point en vis, le lisser.
*/
public function toScrew():void
{
toItem(Vis).smoothing = true;
}

private function toItem(Item:Class):Bitmap
{
if (numChildren != 0)
(removeChildAt(0) as Bitmap).bitmapData.dispose();

var Image:Bitmap = new Item();

//Centrer le clou
Image.x = -Image.width / 2;
Image.y = -Image.height / 2;
addChild(Image);

return Image;
}

/**
* Fonction de debug
* @return la trace du point.
*/
public override function toString():String
{
return Num.toString();
}



/***********************
* FONCTIONS STATIQUES
*/

/**
* Calcule l'angle entre la droite formée par les deux points et l'horizontale.
* Valeur de retour comprise entre 0 et 2PI.
* Note : l'ordre des paramètres est important !
* Note : utilise la fonction atan2. Cf. http://fr.wikipedia.org/wiki/Atan2
* @param P1 le premier point
* @param P2 le second point
* @return l'angle (P1P2,x)
*/
public static function getAngle(P1:Point, P2:Point):Number
{
var Angle:Number = -Math.atan2(P2.y - P1.y, P2.x - P1.x);
if (Angle < 0)
Angle += 2 * Math.PI;

return Angle;
}

/**
* Calcule l'angle entre la droite formée par les deux points et l'horizontale.
* Valeur de retour comprise entre -PI et PI, notation anglaise.
* Note : l'ordre des paramètres est important !
* Note : utilise la fonction atan2. Cf. http://fr.wikipedia.org/wiki/Atan2
* @param P1 le premier point
* @param P2 le second point
* @return l'angle (P1P2,x)
*/
public static function getDirectAngle(P1:Point, P2:Point):Number
{
var Angle:Number = Math.atan2(P2.y - P1.y, P2.x - P1.x);

return Angle;
}

/**
* Distance pythagoréenne entre deux points.
* @param P1 le premier point
* @param P2 le deuxième point
* @return la distance calculée avec le théorème de Pythagore.
*/
public static function getDistance(P1:Point, P2:Point):Number
{
return Math.sqrt(Math.pow(P1.x - P2.x, 2) + Math.pow(P1.y - P2.y, 2));
}
}

}

Package Web

Toute la gestion de la toile (décrit dans cet article).

Code source : Web/HookManager.as
?package Web
{
import flash.display.Shape;
import flash.display.Sprite;

/**
* Dessine une forme entourant tous les points contenus dans la tableau statique Point.Points
* Utilise l'agorithme dit "de la marche de Jarvis" ou "du papier cadeau".
* @author Neamar
*/
public class HookManager extends Sprite
{
public static var WILL_DRAW:String = "willDraw";
/**
* Liste des accroches par lequel passe le fil.
* Structure de l'objet :
* .Point : le point d'accroche
* .Angle : l'angle d'arrivée
* .Sens : +1 ou -1 selon le sens d'arrivée de l'angle.
*/
private var Hooks:Vector.<Hook> = new Vector.<Hook>();

/**
* Liste des points disponibles
*/
protected var Points:Vector.<Point> = new Vector.<Point>();

/**
* La corde des points déjà accrochés par un hook.
*/
protected var Corde:Rope = new Rope();

public function HookManager(Points:Vector.<Point>)
{
this.Points = Points;

addChild(Corde);
}

public function destroy():void
{
Hooks = null;
}

/**
* Ajoute un nouveau point d'accroche.
* @param P le point d'accroche
* @param Angle l'angle sur lequel le fil s'est enroulé
* @param Sens dans quel sens le fil s'est enrolé (-1 ou +1)
*/
protected function addHook(P:Point, Angle:Number, Sens:int):void
{
Hooks.push(new Hook(P, Angle, Sens));

//Dessiner l'emplacement sur lequel on interceptera les évenements souris.
this.graphics.clear();
this.graphics.beginFill(0x000000,.001);
this.graphics.drawRect(0, 0, Main.WIDTH,Main.HEIGHT);
this.graphics.endFill();

//Redessiner la forme
drawShape();
}

/**
* Supprimer le dernier hook.
* Cette fonction ne fait rien si on tente de supprimer le dernier hameçon.
* @return true si la suppression a été effectuée.
*/
protected function removeHook():Boolean
{
if (Hooks.length > 1)
{
Hooks.pop();
//Redessiner la forme
drawShape();
return true;
}
else
return false;
}

/**
* Récupérer le dernier hameçon planté.
* Contrat : le premier hook a été correctement placé.
* @return le dernier élément enregistré.
*
*/
protected function getHook():Hook
{
return Hooks[Hooks.length-1];
}

protected function getHooksLength():int
{
return Hooks.length;
}

/**
* Dessine une forme passant par les points spécifiés dans le tableau Enveloppe.
*/
protected final function drawShape():void
{
Corde.clear();

if (Hooks.length == 0)
return;

var P:Point = Hooks[0].P;
Corde.moveTo(P);

for each(var A:Hook in Hooks)
{
//Permettre la modification des couleurs si quelqu'un veut listener la dessus.
var E:CustomEvent = new CustomEvent(HookManager.WILL_DRAW);
E.drawer = Corde;
E.From = P;
E.To = A.P;
if(E.From != E.To)
dispatchEvent(E);
P = A.P;

Corde.lineTo(P);
}

this.graphics.lineStyle(3, 0xbd9969,.8);
}
}
}

Code source : Web/Eulris.as
?package Web
{
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;

/**
* Permet la gestion de l'accroche / décroche d'un fil à l'aide des méthodes fournies par HookManager.
* Utilise un dérivé de l'agorithme dit "de la marche de Jarvis" ou "du papier cadeau".
* Triggere des évenements lors de l'accroche et de la décroche.
* @author Neamar
*/
public class Eulris extends HookManager
{
public static const FIRST_HOOK_ADDED:String = "firstHookAdded";
public static const HOOK_ADDED:String = "hookAdded";
public static const HOOK_REMOVED:String = "hookRemoved";

private var Araignee:Point = new Point(mouseX, mouseY);
private var BoutCorde:Rope = new Rope();
private var Angles:Vector.<Position> = null;

private var DernierAngle:Number = 0;
private var DerniereDistance:Number = 0;

public function Eulris(Points:Vector.<Point>)
{
super(Points);

//Laisser l'utilisateur choisir le point initial
for each(var P:Point in Points)
{
P.mouseEnabled = true;
P.addEventListener(MouseEvent.CLICK, addFirstHook);
addChild(P);
}

//Ajouter le bout de corde, masquer l'araignée et un peu d'ombre.
addChild(BoutCorde);
setChildIndex(BoutCorde, 0);
setChildIndex(Corde, 0);
Araignee.visible = false;
this.filters = Main.Ombre;

}

/**
* Détruit l'objet, libère la mémoire (du moins, dans la limite autorisée par Flash) et rend la main.
*/
public final override function destroy():void
{
//Nettoyage des objets graphiques.
Araignee.graphics.clear();
graphics.clear();
while (numChildren != 0)
removeChildAt(0);

//Suppression des listeners :
for each(var P:Point in Points)
P.removeEventListener(MouseEvent.CLICK, addFirstHook);
removeEventListener(MouseEvent.MOUSE_MOVE, moveAraignee);

//Mise à null de tout le monde pour avoir des erreurs si l'objet continue d'être utilisé.
Araignee = null;
Angles = null;

//Puis destruction du super :
super.destroy();
}

/**
* Une fois le noeud choisi, démarrer le parcours avec l'araignée.
* @param e On se sert uniquement de e.target
*/
public function addFirstHook(e:MouseEvent):void
{

//Supprimer les listeners et réduire les noeuds.
for each(var P:Point in Points)
{
P.removeEventListener(MouseEvent.CLICK, addFirstHook);
P.mouseEnabled = false;
}

//Créer le premier hameçon (indécrochable, donc l'angle n'est pas important) :
addHook(e.target as Point, 0, 0);
Araignee.x = mouseX;
Araignee.y = mouseY;
Araignee.mouseEnabled = false;
DernierAngle = Point.getAngle(getHook().P, Araignee);
DerniereDistance = Point.getDistance(getHook().P, Araignee);

addEventListener(MouseEvent.MOUSE_MOVE, moveAraignee);

var E:CustomEvent = new CustomEvent(FIRST_HOOK_ADDED);
E.newHook = getHook();
dispatchEvent(E);
}

/**
* Déconnecte la souris du niveau, précurseur de la destruction...
*/
public function disconnect():void
{
Araignee.graphics.clear();
removeEventListener(MouseEvent.MOUSE_MOVE, moveAraignee);
}

/**
* Met à jour la position de l'araignée en cas de déplacement souris.
* Calcule s'il faut accrocher le noeud à 'autres endroits.
* Ou le décrocher.
* Puis met à jour la toile.
* @param e
*/
private function moveAraignee(e:MouseEvent):void
{

//Déplacer l'araignée
Araignee.x = e.localX;
Araignee.y = e.localY;

//Variables utiles aux calculs qui vont suivre
var DernierHook:Hook;
var AngleActuel:Number;
var AngleMin:Number;
var AngleMax:Number;
var Distance:Number;
var Sens:int;//Sens trigo // anti trigo

/**
* Calculer les variables nécessaires aux calculs.
*/
function computeProperties():void
{
DernierHook = getHook();
AngleActuel = Point.getAngle(DernierHook.P, Araignee);
AngleMin = Math.min(AngleActuel, DernierAngle);
AngleMax = Math.max(AngleActuel, DernierAngle);
Distance = Point.getDistance(DernierHook.P, Araignee);
Sens = (AngleActuel > DernierAngle)?1: -1;//Sens trigo // anti trigo

if (AngleMax > 5 && AngleMin < 1)
{
//On a tourné autour du cercle, il faut inverser les min et max et modifier l'angleMin pour ne pas avoir un saut de 2PI.
var Swap:Number = AngleMax;
AngleMax = AngleMin;
AngleMin = Swap - 2 * Math.PI;
Sens = -Sens;
}
}

computeProperties();



//Si les distances sont trop grandes, y a un problème (sortie de la souris par exemple) et on ne prend pas en compte le mouvement.
if (Math.abs(DerniereDistance-Distance) < 100)
{
//Faut-il ajouter un hook ?
//Liste des hameçons ajoutés sur cette itération, pour ne pas ajouter à l'infini (si on déplace très vite la souris) :
var Ajoutes:Vector.<Point> = new Vector.<Point>();

//Faire une boucle for pour faire un reset facile du tableau en cas d'jaout de Hameçons. Foreach agit sur une référence :\
for (var i:int = 0; i < Angles.length;i++)
{
var Item:Position = Angles[i];

if (Item.P != DernierHook.P && Item.Distance <= Distance && Item.Angle >= AngleMin && Item.Angle <= AngleMax && Ajoutes.indexOf(Item.P)==-1)
{
//On a trouvé un hameçon ! L'enregistrer et mettre à jour le dernierHook
addHook(Item.P, Item.Angle, Sens);

Ajoutes.push(Item.P);

//Si l'ajout du hook a entraîné la victoire du niveau et sa destruction, stopper.
if (Araignee == null)
return;

//Remettre à jour les variables.
i = 0;
computeProperties();
}
}

//Faut-il enlever un hook ?
while(DernierHook.Sens != Sens && DernierHook.Angle >=AngleMin && DernierHook.Angle<=AngleMax)
{
if(removeHook())
{DernierHook.P.scaleY = 1; DernierHook = getHook(); }
else
break;
}
}

//Enregistrer et dessiner.
DernierAngle = AngleActuel;
DerniereDistance = Distance;

BoutCorde.clear();
//BoutCorde.graphics.lineStyle(3, 0xbd9969,.8);
BoutCorde.moveTo(DernierHook.P);
BoutCorde.lineTo(Araignee);
}

/**
* Mettre à jour les positions des angles lors de l'ajout d'une accroche.
*/
protected override final function addHook(P:Point, Angle:Number, Sens:int):void
{
//Le dispatch de l'event renverra le dernier hook :
var E:CustomEvent = new CustomEvent(HOOK_ADDED);
if(getHooksLength()>0)
E.lastHook = getHook();
super.addHook(P, Angle, Sens);


updateAngles();

E.newHook = getHook();
//Ne dispatcher l'event que si on a au moins deux hooks
if(getHooksLength()>1)
dispatchEvent(E);

//Redessiner la forme. On le fait une deuxième fois (ça a déjà était fait dans le super) pour prendre en compte les modifications du dernier dispatchEvent.
drawShape()
}

/**
* Mettre à jour la position des angles lors de l'ajout d'une accroche
*/
protected override final function removeHook():Boolean
{
var E:CustomEvent = new CustomEvent(HOOK_REMOVED);
E.lastHook = getHook();

var B:Boolean = super.removeHook();
updateAngles();

E.newHook = getHook();
//Ne dispatcher l'event que si il y a bien eu modification
if(B)
dispatchEvent(E);

//Redessiner la forme. On le fait une deuxième fois (ça a déjà était fait dans le super) pour prendre en compte les modifications du dernier dispatchEvent.
drawShape()

return B;
}

/**
* Calculer les angles de chaque noeud par rapport à l'accroche courante.
*/
private function updateAngles():void
{
Angles = new Vector.<Position>();
var Accroche:Point = getHook().P;
for each(var P:Point in Points)
{
Angles.push(new Position(P, Point.getAngle(Accroche, P), Point.getDistance(P,Accroche)));
}

Angles.sort(sortAngle);
}

private function sortAngle(A:Position, B:Position):Number
{
if (A.Distance < B.Distance)
return -1;
else if (A.Distance > B.Distance)
return 1;
else
return 0;
}
}
}

/**
* Classe Helper pour stocker les angles dans une structure facile d'accès.
*/
class Position
{
public var P:Point;
public var Angle:Number;
public var Distance:Number;

/**
*
* @param P le point concerné
* @param Angle l'angle que fait le point avec le point d'accroche courant
* @param Distance la distance entre P et le point d'accroche courant.
*/
public function Position(P:Point, Angle:Number, Distance:Number)
{
this.P = P;
this.Angle = Angle;
this.Distance = Distance;
}

public function toString():String
{
return "[" + Math.round(Distance) + "," + Angle + "]";
}
}
Code source : Web/Hook.as
?package Web
{

/**
* Indique un point d'accroche pour le fil.
* @author Neamar
*/
public class Hook
{
public var P:Point;
public var Angle:Number;
public var Sens:int;

/**
* Construit un objet d'accroche du fil.
* @param P le point d'accroche
* @param Angle l'angle sur lequel le fil s'est enroulé
* @param Sens dans quel sens le fil s'est enrolé (-1 ou +1)
*/
function Hook(P:Point,Angle:Number,Sens:int)
{
this.P = P;
this.Angle = Angle;
this.Sens = Sens;
}
}

}

Package Level

Gestion des différents types de niveaux.

Code source : Levels/Level.as
?package Levels
{
import com.greensock.TweenLite;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;//Interaction utilisateur//souris
import flash.filters.BitmapFilter;
import flash.utils.Dictionary;
import Web.Eulris;
import Web.HookManager;

import flash.filters.GlowFilter;
import flash.text.TextField;//Champ de texte. Classe assez vaste, utilisée uniquement pour l'affichage de texte ici. Peut être du texte au format HTML.

/**
* Un niveau de jeu abstrait.
* Prend en paramètre le graphe au format décrit dans E-Ditor (sans points de contrôles)
* Exemple 0,0|100,100:0,1 crée deux noeuds (aux coordonnées 0,0 et 100,100) et place un trait entre les noeuds 0 et 1.
* Dispatche l'evénement LEVEL_WIN une fois le niveau gagné.
* Les tests de AretesInitiales se font avec la fonction abstraite checkVictory() qui est appelée à chaque ajout de noeuds sur le graphe des Hooks.
*/
public class Level extends Sprite
{
//Evénement dispatché à la fin du niveau
public static const LEVEL_WIN:String = "levelWin";
//Evénement dispatché si le niveau est recommencé
public static const LEVEL_RESTART:String = "levelRestart";

/**
* Le graphique avec les traits gris indiquant le Graphe à atteindre
*/
public var Graphe:Shape = new Shape();

/**
* Liste des points définissant le niveau
*/
public var Points:Vector.<Point> = new Vector.<Point>();

/**
* Graphe défini par les conditions initiales (arêtes de départ)
*/
public var AretesInitiales:Dictionary = new Dictionary();

/**
* Liste des arêtes actuellements tracées. Le jeu s'arrête quand AretesInitiales == Aretes
*/
public var Aretes:Dictionary = new Dictionary();

/**
* Gestion des déplacements
*/
public var Toile:Eulris;

/**
* Initialise le niveau et les éléments graphiques qui le composent.
* Appelle la méthode createLevel() pour générer les données du graphe de base.
* @param Datas Les données au format E-Ditor.
*/
public function Level(Datas:String)
{

//Cas des niveaux "directs".
if (Datas == "")
return;

var Niv_String:String=Datas;
var Composants:Array;
var Part:Array=Niv_String.split(":");
var strNoeuds_Array:Array=Part[0].split("|");
var strArc_Array:Array=Part[1].split("|");
var Noeuds:Array=new Array();
var Liens:Array=new Array();
for each(var Node:String in strNoeuds_Array)
{
Composants=Node.split(",");
Noeuds.push(Composants);
}
for each(var Arete:String in strArc_Array)
{
Composants=Arete.split(",");
Liens.push(Composants);
}

createLevel(Noeuds, Liens);

addEventListener(Event.REMOVED_FROM_STAGE, destroy);
}

/**
* Détruit le niveau et le rend inutilisable !
*/
public function destroy(e:Event=null):void
{
//Nettoyage des objets graphiques.
Graphe.graphics.clear();
graphics.clear();
Toile.destroy();
while (numChildren != 0)
removeChildAt(0);

//Suppression des listeneres :
removeEventListener(Event.REMOVED_FROM_STAGE, destroy);
Toile.removeEventListener(Eulris.FIRST_HOOK_ADDED, firstHookAdded);
Toile.removeEventListener(Eulris.HOOK_ADDED, hookAdded);
Toile.removeEventListener(Eulris.HOOK_REMOVED, hookRemoved);
Toile.removeEventListener(MouseEvent.DOUBLE_CLICK, restart);

//Mise à null de tout le monde
Points = null;
Aretes = AretesInitiales = null;
Toile = null;
Graphe = null;
}

/**
* Recommence le niveau.
* @param e
*/
public function restart(e:Event = null):void
{
initEulris();
}

/**
* Créer le niveau en insérant dans le bon ordre les noeuds
* @param Noeuds La liste des noeuds au format <x,y>
* @param Liens La liste des arêtes au format <noeud1,noeud2>
*/
private function createLevel(Noeuds:Array,Liens:Array):void
{
addChild(Graphe);
setChildIndex(Graphe, 0);

//Le numéro du noeud.
var i:int = 0;
for each(var Arr_Noeud:Array in Noeuds)
{
Points.push(new Point(Arr_Noeud[0], Arr_Noeud[1],i));
AretesInitiales[Points[Points.length-1]] = new Vector.<Point>();
Aretes[Points[Points.length - 1]] = new Vector.<Point>();
i++
}

initEulris();

Graphe.graphics.clear();
Graphe.graphics.lineStyle(1, 0xDDDDDD);
Graphe.filters = Background.Instance.Deplacement;

//Dessin de l'image de fond.
for each(var Arr_Lien:Array in Liens)
{
//Dessiner les traits gris désignants l'Graphe
Graphe.graphics.moveTo(Points[Arr_Lien[0]].x, Points[Arr_Lien[0]].y);
Graphe.graphics.lineTo(Points[Arr_Lien[1]].x, Points[Arr_Lien[1]].y);

//Contrôle des redondances.
if (AretesInitiales[Points[Arr_Lien[0]]].indexOf(Points[Arr_Lien[1]]) != -1)
throw new Error("Niveau mal formé (" + Arr_Lien[0] + "," + Arr_Lien[1] + ").");

//Et enregistrer l'état à atteindre pour la AretesInitiales :
AretesInitiales[Points[Arr_Lien[0]]].push(Points[Arr_Lien[1]])
AretesInitiales[Points[Arr_Lien[1]]].push(Points[Arr_Lien[0]])
}

//Un peu d'optimisation, la figure est quand même relativement complexe (surtout son filtre)
Graphe.cacheAsBitmap = true;
}

/**
* Exporte la matrice d'adjacence du graphe.
* @return la matrice, séparée par des " " et des \n.
*/
public function exportAdjacencyMatrix():String
{
var O:String = "";
for each(var P:Point in Points)
{
for each(var P2:Point in Points)
O += " " + (AretesInitiales[P].indexOf(P2) == -1?0:1);
O += "\n";
}
return O;
}

/**
* Construit des données à partir d'une matrice d'adjacence.
* @param Matrix la matrice
* @return des données suffisantes pour créer un niveau.
*/
public static function fromAdjacencyMatrix(Matrix:String,LigneSeparator:String="\n",ColumnSeparator:String=" "):String
{
var Retour:String = ":";
var Lignes:Array = Matrix.split(LigneSeparator);
for (var i:int = 0; i < Lignes.length;i++)
{
var Cellules:Array = Lignes[i].split(ColumnSeparator);
//S'arrêter sur la diagonale, sinon on va doublonner.
for (var j:int = 0; j < i; j++)
{
if (Cellules[j] == "1")
Retour += i + "," + j + "|";
}
}
return Retour;
}

/**
* Initialise (réinitialise) un objet Eulris pour le niveau.
*/
protected function initEulris(e:Event=null):void
{
if (Toile != null)
{
removeChild(Toile);
Toile.disconnect();
Toile.destroy();
Toile.removeEventListener(Eulris.FIRST_HOOK_ADDED, firstHookAdded);
Toile.removeEventListener(Eulris.HOOK_ADDED, hookAdded);
Toile.removeEventListener(Eulris.HOOK_REMOVED, hookRemoved);
Toile.removeEventListener(HookManager.WILL_DRAW, checkValidity);
Toile.removeEventListener(MouseEvent.DOUBLE_CLICK, restart);


//Réinitialiser les points parcourus pour ne pas tricher ;)
for each(var P:Point in Points)
Aretes[P] = new Vector.<Point>();
}

Toile = new Eulris(Points);
Toile.doubleClickEnabled = true;
addChild(Toile);
Toile.addEventListener(Eulris.FIRST_HOOK_ADDED, firstHookAdded);
Toile.addEventListener(MouseEvent.DOUBLE_CLICK, restart);
Toile.addEventListener(HookManager.WILL_DRAW, checkValidity);

setChildIndex(Toile, 1);//Au dessus de Graphe, en dessous des noeuds.
setChildIndex(Graphe, 0);

dispatchEvent(new Event(LEVEL_RESTART));
}

/**
* Déclenché une fois le noeud initial sélectionné.
* @param e
*/
private function firstHookAdded(e:Event):void
{
Toile.removeEventListener(Eulris.FIRST_HOOK_ADDED, firstHookAdded);
Toile.addEventListener(Eulris.HOOK_ADDED, hookAdded);
Toile.addEventListener(Eulris.HOOK_REMOVED, hookRemoved);
}

/**
* Déclenché à chaque ajout d'un hook sur le graphe.
*
* @param e possède deux propriétés en plus d'un simple Event : lastHook:Hook et newHook:Hook
*/
private function hookAdded(e:CustomEvent):void
{
//Pour les tricheurs ; on place ici le chemin calculé par un algorithme (ici, la solution du dernier niveau)
//Et on a une petite animation qui indique les points à relier à tout moment :)
//ça aide :)
//var Solution:Array = [1,15,10,26,23,2,14,22,27,9,16,20,29,7,18,31,5,11,25,24,12,13,3,6,30,19,17,32,4,21,28,8];
//TweenLite.to(Points[Solution[Solution.indexOf(e.newHook.P.Num + 1) + 1] - 1], 1.5, { scaleX:.5 } );

Aretes[e.lastHook.P].push(e.newHook.P);
Aretes[e.newHook.P].push(e.lastHook.P);

checkVictory();
}

/**
* Déclenché à chaque suppression d'un hook
*
* @param e possède deux propriétés en plus : lastHook:Hook et newHook:Hook
*/
private function hookRemoved(e:CustomEvent):void
{
//Supprimer les points du tableau
Aretes[e.lastHook.P].splice(Aretes[e.lastHook.P].indexOf(e.newHook.P),1);
Aretes[e.newHook.P].splice(Aretes[e.newHook.P].indexOf(e.lastHook.P), 1);
}


/**
* Fonction abstraite permettant de déterminer si la victoire a été obtenue ou non.
*/
protected function checkVictory():void
{
throw new Error("Appel d'une méthode abstraite.");
}

/**
* Fonction permettant d'interagir dynamiquement lors du rendu de la corde.
* Modifie les paramètres de dessin (disponibles dans e.drawer).
* @return true si l'arête est correcte. Utilisé pour l'héritage.
*/
protected function checkValidity(e:CustomEvent):Boolean
{
//Test arête correcte (disponible dans le graphe)
//ET arête non en double
if (AretesInitiales[e.From].indexOf(e.To) != -1
&& Aretes[e.To].indexOf(e.From) == Aretes[e.To].lastIndexOf(e.From))
{
//e.drawer.lineStyle(3, 0xbd9969, .8);
e.drawer.Etat = e.drawer.OK;
return true;
}
else
{
//e.drawer.lineStyle(3, 0xbd0000, .8);//Arête INCORRECTE
e.drawer.Etat = e.drawer.Erreur;
return false;
}
}
}
}
Code source : Levels/TextLevel.as
?package Levels
{
import com.greensock.TweenLite;
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.filters.DropShadowFilter;
import flash.text.TextField;
import flash.text.TextFormat;

/**
* Un niveau avec de l'aide.
* Pas vraiment utile d'un point de vue conceptuel, mais pratique pour séparer les concepts et ne pas tout balancer sur Level.
* @author Neamar
*/
public class TextLevel extends Level
{
private static var TextEffet:Array = new Array(new DropShadowFilter(1, 40, 0, 1, 1, 1, 1, 1, true));
[Embed(source = "../../assets/Laouib.ttf", fontFamily = "Laouib", fontWeight = "bold", mimeType="application/x-font-truetype")]
private static var EmbedFont:String;

private static var Aide_format:TextFormat = new TextFormat();
{
Aide_format.font = "Laouib";
Aide_format.size = 18;
Aide_format.bold = true
Aide_format.color = 0xd2cdc3;
}
protected var Aide:TextField = new TextField();
protected var AideEffet:Sprite = new Sprite();

protected var Infos:TextField = new TextField();
protected var InfosEffet:Sprite = new Sprite();

/**
* Constructeur du niveau.
* @param Datas cf. super()
* @param Texte le texte à afficher au format HTML.
*/
public function TextLevel(Datas:String,Texte:String="")
{
super(Datas);

//Numéro du niveau.
Infos.x=60;
Infos.y = 65;
Infos.width = 500;
Infos.height = 400;
initTextField(Infos, InfosEffet);
InfosEffet.filters = Background.Instance.Deplacement;

//Texte d'aide.
if (Texte != "")
{
Aide.x=60;
Aide.y = 65;
Aide.width = 500;
Aide.height = 400;

initTextField(Aide, AideEffet);
Aide.multiline = true;
Aide.wordWrap = true;

AideEffet.filters = TextEffet;

Text = Texte;
}
}

public override function destroy(e:Event=null):void
{
Aide.htmlText = Infos.htmlText = "";

if (contains(AideEffet))
removeChild(AideEffet);
removeChild(InfosEffet);
AideEffet.filters = InfosEffet.filters = null;
Aide = Infos = null;
}

/**
* Afficher un texte d'aide.
* @param Texte en HTML
*/
public function set Text(Texte:String):void
{
if (!contains(AideEffet))
throw new Error("Ce niveau n'a pas été initialisé pour afficher du texte.");
Aide.htmlText = Texte;
}

/**
* Afficher le numéro du niveau.
* @param Texte brut.
*/
public function set Caption(Texte:String):void
{
Infos.text = Texte;
Infos.x = Main.WIDTH - Infos.textWidth - 20;
Infos.y = Main.HEIGHT - Infos.textHeight;
}

/**
* Initialise un Textfield pour qu'il ait le style par défaut.
* @param T le textfield
* @param Container son container
*/
protected function initTextField(T:TextField,Container:Sprite):void
{
T.embedFonts = true;
T.defaultTextFormat = Aide_format;
//T.autoSize = "left";
T.thickness=3;
T.selectable=false;
T.mouseEnabled = Container.mouseEnabled = false;
Container.addChild(T);
addChild(Container);

//Toujours garder l'effet en bas, sinon on ne peut plus cliquer sur les noeuds
function setEffetUnderEverything(e:Event=null):void
{
setChildIndex(Container, 0);
}
addEventListener(Level.LEVEL_RESTART, setEffetUnderEverything);
setEffetUnderEverything();
}
}

}
Code source : Levels/EulerPathLevel.as
?package Levels
{
import flash.events.Event;

/**
* Un niveau que l'on gagne en créant un circuit eulérien sur le graphe, i.e en parcourant toutes les arêtes en une seule fois.
* @author Neamar
*/
public class EulerPathLevel extends TextLevel
{
/**
* Constructeur.
* @param Datas cf. super()
* @param Aide Le texte d'aide à afficher au format HTML.
*/
public function EulerPathLevel(Datas:String,Aide:String="")
{
super(Datas,Aide);
}

/**
* Compare les dictionnaires Aretes et AretesInitiales. Si chacune de leurs clés sont égales, dispatch l'evenement LEVEL_WIN.
*/
protected override final function checkVictory():void
{
var P:Point;

//Fast test : juste comparer les tailles des tableaux.
//Complexité : O(n)
for each(P in Points)
{
//Si les tailles diffèrent on peut s'arrêter tout de suite.
if (Aretes[P].length != AretesInitiales[P].length)
return;
}

//Si tous les tableaux sont égaux, il faut être plus exhaustif et vérifier que le contenu de chaque tableau est similaire aux autres.
//Ici l'ordre n'a aucune importance : les tableaux sont donc des ensembles.
//Mathématiquement pour prouver que A = B il faut prouver que A est inclus dans B ET QUE B est inclus dans A.
//Ici cependant on va utiliser deux propriétés spécifiques de nos ensembles :
// - ils font la même taille
// - le contenu du tableau AretesInitiales[x] est unique (pas de doublons si le niveau est bien conçu).
//Il suffit alors de prouver que tous les élements de AretesInitiales[x] sont dans Aretes[x], et ce pour tout x.
//Complexité théorique O(n^3) (tous les points, tous les liens entre ces points, + une recherche avec indexOf)
for each(P in Points)
{
for each(var P2:Point in AretesInitiales[P])
{
if(Aretes[P].indexOf(P2)==-1)
return;
}
}

dispatchEvent(new Event(LEVEL_WIN));

}
}

}
Code source : Levels/HamiltonLevel.as
?package Levels
{
import com.greensock.TweenLite;
import flash.display.Bitmap;
import flash.events.Event;
import flash.events.MouseEvent;
import Web.Eulris;

/**
* Un niveau que l'on gagne en créant un circuit hamiltonien sur le graphe, i.e en parcourant tous les somments en une seule fois, sans passer deux fois par le même sommet et en utilisant les arêtes du graphe.
* @author Neamar
*/
public class HamiltonLevel extends TextLevel
{
private var Premier:Point;

public function HamiltonLevel(Datas:String,Aide:String="")
{
super(Datas, Aide);

Toile.addEventListener(Eulris.FIRST_HOOK_ADDED, drawFirstHook);
addEventListener(Level.LEVEL_RESTART, eraseFirstHook);
}

/**
* Redessine le premier noeud selectionné pour l'identifier facilement
* @param e
*/
private function drawFirstHook(e:CustomEvent):void
{
if (Premier != null)
eraseFirstHook();

Premier = (e.newHook.P as Point);

Premier.toScrew();

TweenLite.to(Premier, 1, { rotation:360 } );
}

private function eraseFirstHook(e:Event=null):void
{
if(Premier != null)
TweenLite.to(Premier, 1, { rotation: -360, onComplete: Premier.toNail } );

Premier = null;
Toile.addEventListener(Eulris.FIRST_HOOK_ADDED, drawFirstHook);
}

/**
* Si chaque sommet n'est utilisé qu'une fois, dispatch l'evenement LEVEL_WIN.
*/
protected override final function checkVictory():void
{
var P:Point;

//Fast test : on devrait avoir tous les noeuds qui sont liés à deux sommets (arrivée vers le noeud + départ du noeud).
//Complexité : O(n)
for each(P in Points)
{
//Si les tailles diffèrent on peut s'arrêter tout de suite.
if (Aretes[P].length != 2)
return;
}

//Si tous les noeuds ont deux connexions, il y a un cycle hamiltonien.
//Reste à vérifier que tous les chemins pris sont bien dans la liste des arêtes autorisées !

for each(P in Points)
{
for each(var P2:Point in Aretes[P])
{
if(AretesInitiales[P].indexOf(P2)==-1)
return;//L'arête n'appartient pas au graphe.
}
}

dispatchEvent(new Event(LEVEL_WIN));
}

/**
* Fonction permettant d'interagir dynamiquement lors du rendu de la corde.
* Modifie les paramètres de dessin (disponibles dans e.drawer).
*/
protected override function checkValidity(e:CustomEvent):Boolean
{
//Test arête correcte (disponible dans le graphe)
//ET arête non en double
if (super.checkValidity(e) && Aretes[e.To].length <= 2 && Aretes[e.From].length <= 2)
{
//e.drawer.lineStyle(3, 0xbd9969, .8);
e.drawer.Etat = e.drawer.OK;
return true;
}
else
{
//e.drawer.lineStyle(3, 0xbd0000, .8);//Arête INCORRECTE
e.drawer.Etat = e.drawer.Erreur;
return false;
}
}
}

}
Code source : Levels/AdLevel.as
?package Levels
{
import com.greensock.TweenLite;
import flash.display.Bitmap;
import flash.display.MovieClip;
import flash.events.Event;
import mochi.as3.*;

/**
* Un niveau "publicitaire".
* @author Neamar
*/
public class AdLevel extends TextLevel
{
private var Pub:MovieClip;

public function AdLevel(Datas:String,Pub:MovieClip)
{

super(Datas);

//L'élément dynamique contenant la pub DOIT être placé sur le stage pour que MochiAd puisse récupérer les infos de chargement. S'il n'est pas sur stage, on l'y remet donc :
if(!Pub.stageParent.contains(Pub))
Pub.stageParent.addChild(Pub);

MochiAd.showInterLevelAd( {
clip:Pub,
id:"803e3dbb622e59fc",
res:"640x480",
no_bg:true,
ad_skipped:makeVictory,
ad_finished:makeVictory,
ad_loaded:positionAd
} );

//Puis la replacer sur nous même.
Pub.visible = true;
addChild(Pub);
//Pub.y = -50;
}

public override function destroy(e:Event=null):void
{
//Virer la pub si elle est encore chez nous.
//if (contains(Pub))
//removeChild(Pub);

super.destroy();
}

/**
* Recommence le niveau.
* @param e
*/
public override function restart(e:Event = null):void
{
trace("On ne recommence pas la pub :p");
}

protected final function positionAd(width:Number, height:Number):void
{
trace(width, height);
}

/**
* Dispatch l'evenement LEVEL_WIN.
*/
protected final function makeVictory(e:Event=null):void
{
dispatchEvent(new Event(LEVEL_WIN));
}

}

}
Code source : Levels/CreditsLevel.as
?package Levels
{
import com.greensock.TweenLite;
import flash.display.Bitmap;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.navigateToURL;
import flash.net.URLRequest;

/**
* Le niveau de présentation des crédits
* @author Neamar
*/
public class CreditsLevel extends TextLevel
{
private const DUREE:int = 3;

[Embed(source = "../../assets/Icosien.png")]
private var Logo:Class;
[Embed(source = "../../assets/Neamar.png")]
private var Neamar:Class;
[Embed(source = "../../assets/Licoti.png")]
private var Licoti:Class;



private var Image:Bitmap = new Logo();
private var ImageNeamar:Bitmap = new Neamar();
private var ImageLicoti:Bitmap = new Licoti();

public function CreditsLevel(Datas:String,Texte:String)
{
super(Datas,Texte);

Image.x = (Main.WIDTH - Image.width) / 2;
ImageNeamar.x = (Main.WIDTH - ImageNeamar.width) / 2;
ImageLicoti.x = (Main.WIDTH - ImageLicoti.width) / 2;

Image.y = ImageNeamar.y = ImageLicoti.y = 180;

ImageNeamar.alpha = 0;
ImageLicoti.alpha = 0;
addChild(Image);
addChild(ImageNeamar);
addChild(ImageLicoti);

Aide.x = Main.WIDTH - Aide.textWidth - 20;
Aide.y = Main.HEIGHT - Aide.textHeight;

AideEffet.addEventListener(MouseEvent.CLICK, gotoSponsor);
AideEffet.mouseEnabled = true;
AideEffet.buttonMode = true;
AideEffet.useHandCursor = true;

TweenLite.to(Image, DUREE, { alpha:1, onComplete:switchToNeamar } );
}

public override final function destroy(e:Event=null):void
{
removeChild(Image)
removeChild(ImageNeamar);
removeChild(ImageLicoti);

Image.bitmapData.dispose();
ImageNeamar.bitmapData.dispose();
ImageLicoti.bitmapData.dispose();
Image = ImageLicoti = ImageNeamar = null;
}

/**
* Efface Icosien, passe sur Neamar.
*/
public function switchToNeamar():void
{
TweenLite.to(Image,DUREE,{alpha:0})
TweenLite.to(ImageNeamar, DUREE, { alpha:1, onComplete:switchToLicoti } );
}

/**
* Efface Neamar, passe sur Licoti.
*/
public function switchToLicoti():void
{
removeChild(Image);
TweenLite.to(ImageNeamar, DUREE, { alpha:0 } );
TweenLite.to(ImageLicoti, DUREE, { alpha:1, onComplete:makeVictory } );
}

public function makeVictory():void
{
dispatchEvent(new Event(LEVEL_WIN));
}

public function gotoSponsor(e:Event):void
{
var request:URLRequest = new URLRequest("http://www.mini-jeu-gratuit.fr/vip/neamar/");
try {
navigateToURL(request, '_blank'); //Dans une nouvelle fenêtre (probablement nouvel onglet en fait)
} catch (e:Error) {
trace("Impossible de lancer la fenêtre :(");
}
}
}

}
Code source : Levels/EndLevel.as
?package Levels
{
import com.greensock.TweenLite;
import flash.display.Bitmap;
import flash.display.Loader;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.navigateToURL;
import flash.net.URLRequest;
import flash.system.LoaderContext;

/**
* Le niveau de présentation des crédits
* @author Neamar
*/
public class EndLevel extends Level
{
[Embed(source = "../../assets/Fin.jpg")]
private static var Fin:Class;
private static var Img_Fond:Loader;

private var Image:Loader = Img_Fond;

public function EndLevel(Datas:String)
{
//Écraser les Datas et les remplacer par le positionnement des clous.
Datas = "1000,1000|1001,1001|218,45|294,33|361,13|439,25|48,243|176,276|341,276|560,237:0,1";
super(Datas);


if (Image == null)
{//On arrive probablement directement sur ce niveau, et le fichier n'a pas été chargé. Faire ça en urgence !
downloadDatas();
Image = Img_Fond;
}

addChild(Image);
setChildIndex(Image, 0);
}

/**
* Rarement appelé, sauf si on fait touche gauche pour revenir au niveau précédent.
* @param e
*/
public override final function destroy(e:Event=null):void
{
if(contains(Image))
removeChild(Image);
Image = null;
}

/**
* Initialise (réinitialise) un objet Eulris pour le niveau.
*/
protected override function initEulris(e:Event=null):void
{
super.initEulris(e);
if (Image != null)
{
addChild(Image);
setChildIndex(Image, 0);
}
}

/**
* Si le mec s'amuse à joueur, s'assurer qu'il ne peut pas gagner :)
*/
protected override final function checkVictory():void
{
}

/**
* L'image finale est relativement lourde, et rarement vue (malheureusement).
* Plutôt que de l'inclure dans le swf, elle est donc téléchargée dynamiquement si la progression de l'utilisateur laisse croire qu'il va s'en sortir (bref, qu'il arrive sur l'avant dernier niveau :))
*/
public static function downloadDatas():void
{
Img_Fond = new Loader()
Img_Fond.load(new URLRequest("http://neamar.fr/Res/Icosien/Images/assets/Fin.jpg"),new LoaderContext(true));
}


}

}

Bonus

En bonus, le fichier de résolution décrit sur cet article.

Code source : Solver.as
package
{
import Levels.HamiltonLevel;
/**
* R?sout un niveau de jeu.
*/
public class Solver
{
/**
* Initialise le solver.
*/
public function Solver()
{}

/**
* R?sout un niveau hamiltonien en ajoutant les hooks n?cessaires.
*/
public function solveHamilton(ToSolve:HamiltonLevel):void
{
var i:int = 0;
var Datas:Array = new Array();
for(var P:* in ToSolve.AretesInitiales)
{
Datas[ToSolve.Points.indexOf(P)] = new Vector.<int>();
for each(var P2:Point in ToSolve.AretesInitiales[P])
Datas[ToSolve.Points.indexOf(P)].push(ToSolve.Points.indexOf(P2));
}
for (i = 0; i < ToSolve.Points.length; i++)
trace('[' + i + ']' + '=>' + Datas[i]);

trace("Statut : " + solveHamiltonian(Datas));
}

/**
* Trouve un cycle Hamiltonien (s'il existe !) dans le graphe pass? en param?tres.
* Se base sur un algorithme r?cursif.
* Prend en param?tre une liste de noeuds, tel que Noeuds[i] contienne tous les noeuds li?s ? i.
* @param Noeuds:Vector.<Vector.<int>> la liste des points composant le niveau. Typ? comme array car les vector ne sont pas sparse.
* @param Actuel le noeud sur lequel on est actuellement plac?. Non sp?cifi? au premier appel.
* @param LongueurCycle la longueur du cycle actuelle. Si longueur == noeuds.length, c'est fini :)
* @return true si un cycle a ?t? trouv?, false sinon.
*/
private function solveHamiltonian(Noeuds:Array,Actuel:int=0,LongueurCycle:int=0):Boolean
{
//R?cup?rer la liste des itin?raires possibles depuis le noeud Actuel.
var Possibilites:Vector.<int> = Noeuds[Actuel];
//Supprimer ce noeud de la liste (pour qu'on ne le reprenne pas)
Noeuds[Actuel] = null;

//Examiner chacun des itin?raires disponibles depuis ce noeud.
for each(var Possibilite:int in Possibilites)
{
//Si :
// - on a la longueur d'un cycle, et on peut rejoindre 0 (le point de d?part)
// - le noeud n'a pas encore ?t? pris, et qu'on peut trouver un chemin hamiltonien dessus :
if((LongueurCycle==Noeuds.length-1 && Possibilite == 0) ||
(Noeuds[Possibilite]!=null && solveHamiltonian(Noeuds,Possibilite,LongueurCycle+1)))
{
//Chemin trouv? !
trace("Trouv? : " + Actuel);
return true;
}
}

//Sinon, aucun chemin hamiltonien ne passe par cette combinaison. Remettre le tableau dans son ?tat initial, puis quitter et revenir ? la fonction pr?c?dente.
Noeuds[Actuel] = Possibilites;
return false;
}

private function solveEulerian(Noeuds:Array, Actuel:int, Cycle:Vector.<int>):void
{}
}
}
Auteur
Neamar
Date
Juin 2010
But
Finir le jeu
Voir aussi
Compiler l'AS3
Retour
Retour à Icosien
Menu
Index des ressources

Chargement du sommaire...