Dans les égouts, ça va


12

J'ai rencontré une fois ce (mini) jeu où vous aviez 4 tuyaux verticaux ou plus qui étaient reliés par un certain nombre de tuyaux horizontaux et vous devez laisser tomber une balle ou de l'eau dans les tuyaux verticaux.
J'en connais 2 sortes:

  • Mettez l'objet dans un seau / panier sous l'une des sorties (devinez quel tuyau pour le jeter)
  • Devinez de quel tuyau proviendra l'objet.

Exemples de tuyaux:

|       |       |-------|  
|-------|       |-------|  
|       |-------|       |  
|-------|-------|-------|  
|       |-------|       |  
|-------|       |-------|  
|-------|       |-------|  
|       |-------|       |  

Règles de base:

  • Lorsque vous vous déplacez à travers un tuyau horizontal, l'objet descendra si possible
  • Lorsque vous vous déplacez à travers un tuyau vertical, l'objet se transformera en tuyau horizontal lorsque cela sera possible.

Votre travail

  • Écrivez un programme qui créera une grille aléatoire de tuyaux (voir des exemples de tuyaux).
  • Il devrait y avoir au moins 4 tuyaux verticaux et une bonne quantité de tuyaux horizontaux au moins 10.
  • La longueur des tuyaux verticaux dépend de vous.
  • Montrez le chemin que l'objet a pris pour atteindre le fond et montrez combien de tours il a fallu pour y arriver.
  • (Facultatif) entrée pour déterminer le point de départ, tuyaux numérotés 1..N de gauche à droite.

Afficher:

 | vertical pipe
 - horizontal pipe
 : vertical pipe used by the object
 = horizontal pipe used by the object
 V Object starting point
 ^ Object ending point

Exemple:

V
:       |       |-------|
:=======:       |-------|
|       :=======:       |
|-----!-:=======:       |
|       :=======:-------|
|-------|       :=======:
|-------|       :=======:
|       :=======:       |
        ^
14 turns were taken to get to the end.

Détails
L'objet entre dans le tuyau 1 et commence à descendre, va à gauche dans le premier tuyau horizontal.
Redescendre et dans le deuxième tuyau suivi d'un demi-tour dans le troisième tuyau.
À la fin du troisième tuyau, vous voyez un point d'exclamation,
ne devrait pas être dans votre résultat, mais je l'ai utilisé pour vous montrer que l'objet aurait pu aller de l'avant.
Cependant, la règle numéro 1 empêche cela.

Le gagnant sera déterminé par vote dans 3 semaines à partir de maintenant 24-02-2014 (jj-mm).

Bon codage ^. ^


7
Que se passe-t-il si l'objet tombe et qu'il y a un tuyau à gauche et à la même hauteur un tuyau à droite?
Howard

2
De plus, comme l'entrée et la sortie sont fixes, je ne pense pas que ce soit une bonne idée d'en faire un concours de popularité .
Howard

@Howard - comment le qualifieriez-vous alors? "Le gagnant sera déterminé par les votes" me semble être un contexte de popularité - il n'y a certainement pas de golf ou d'autres critères de réussite ici, et ce n'est pas une sortie de chaîne fixe
jimbobmcgee

1
@DavidCarraher - qu'en est-il d'un flipper, plutôt que de l'eau? Avec chaque tuyau horizontal étant un peu magnétique? Et ce magnétisme étant activé lorsque la balle atteint la fin de la section de tuyau de descente et éteint lorsqu'elle pénètre dans le tuyau horizontal! Par laser-tripwire. :-) (Aimants, comment fonctionnent-ils?!) (Laser-tripwires, comment fonctionnent- ils ? !!)
jimbobmcgee

1
@Fabinout - il vaut mieux ne pas être des devoirs sanglants! J'ai gaspillé beaucoup trop d'une semaine de travail parfaitement bien assommant ma réponse à cela!
jimbobmcgee

Réponses:


24

Mathematica

Le code 2D génère un graphique montrant le chemin de l'eau. J'ai affiché les nombres de sommets pour une vérification croisée pratique avec l'affichage 3D. Normalement, les nombres de sommets seraient cachés.

Contribution:

r=10;c=7;
result=flow2D[{r,c},3]

En fait, le résultat contient plusieurs objets. Le premier objet,, result[[1]]est le graphique 2D montré ici.

2D


Les tuyaux tridimensionnels sont Tubedes (lignes 3D) tracées dans 3 espaces. Les coordonnées calculées sur la base des sommets du graphique 2D le long d'une 3e coordonnée qui a été générée de manière aléatoire (cela permet aux tuyaux de prendre différentes positions le long de y chaque fois que le code est exécuté.)

Les axes sont affichés pour aider le lecteur à se convaincre que le rendu 3D est bien basé sur le rendu 2D.

L'entrée 3D qui nous intéresse ici est:

Graphics3D[{CapForm[None],verticalPipes,allRungs,Darker@Red,connections},
ImageSize->600,Axes-> True,Ticks->{Range[1,2 r,2],Range[c],Range[10]},ViewPoint->{0,-2,1.5}]

Le nombre ou les lignes, les colonnes et la colonne d'entrée sont extraits du code 2D. Il n'est pas nécessaire de les saisir à nouveau.

3D

Code 2D

Dans les prochains jours, je documenterai et rangerai le code 2D et 3D.

flow2D[{rows_,columns_},startColumn_]:=
Module[{r=rows,c=columns,g,j,h,ends,middle,midcuts,direction="down",turns=0,path,rungs},

   (*complete gridgraph*)
g=GridGraph[{r,c},VertexSize-> Medium,GraphStyle->"Prototype",EdgeStyle->"Thick",
  VertexLabels->"Name",ImagePadding-> 30,ImageSize->470];

(*horizontal pipes that must be removed*)
ends=Table[r(c1-1)+r1\[UndirectedEdge] r(c1)+r1,{c1,1,c-1},{r1,{1,r}}];

(*horizontal pipes to consider removing *)
middle=Table[r(c1-1)+r1\[UndirectedEdge] r(c1)+r1,{c1,1,c-1},{r1,2,r-1}];
midcuts=RandomSample[#,RandomInteger[Round[{r/15,2r/5}]]]&/@middle;

rungs=Flatten[midcuts(*Join[ends,midcuts]*)];

j=EdgeDelete[g,Flatten[Join[ends,midcuts]]];

h[path_]:= Module[{start=path[[-1]],right,left,up,down,newnodes}, 
     {v=NeighborhoodGraph[j,start,1,(*VertexLabels\[Rule]"Name",*)ImagePadding->25],
     VertexList[v]};newnodes=Complement[VertexList[v],path];
     If[newnodes=={},path,  
     h[Append[path,
  Switch[direction,
   "down",Which[
     MemberQ[newnodes,start+r],(turns++;direction="right";start+r),
     MemberQ[newnodes,start-r],(turns++;direction="left";start-r),
     MemberQ[newnodes,start-1],start-1],
   "right",Which[
     MemberQ[newnodes,start-1],(turns++;direction="down";start-1),
     MemberQ[newnodes,start+r],start+r],  
   "left",Which[
     MemberQ[newnodes,start-1],(turns++;direction="down";start-1),
     MemberQ[newnodes,start-r],start-r]
    ]]]]];
{HighlightGraph[j,path=h[{r*startColumn}],ImageSize->300],path,rungs,ends,midcuts}]

convert[node_,r_,c_]:=Append[footing[[Quotient[node-1,r]+1]],Mod[node-1,r]+1(*Mod[node,r]*)]
connect[a_\[UndirectedEdge]b_,r_,c_]:=Tube[Line[{convert[a,r,c],convert[b,r,c]}],0.2]

Code 3D et résultats

r=10;c=7;
result=flow2D[{r,c},3];
g2D=result[[1]];
path2D=result[[2]];
\[AliasDelimiter]
xScale=2;
footing = {#, RandomInteger[{1, 6}]} & /@ Range[1,xScale c, xScale];
verticalPipes=Tube[Line[{Append[#,1],Append[#,r]}],.19]&/@footing;
Graphics3D[{CapForm[None],verticalPipes},ImageSize->600,Axes->True,AxesEdge->Automatic,ViewPoint->{0,-2,1.5},
Ticks->{Range[1,2 r,2],Range[c],Range[10]}];

path3D=UndirectedEdge@@@Partition[Riffle[stops=path2D,Rest@stops],2];
allRungs=connect[#,r,c]&/@rungs;
connections=connect[#,r,c]&/@path3D;

path2D;
g2D
Graphics3D[{CapForm[None],verticalPipes,allRungs,Darker@Red,connections},
ImageSize->600,Axes-> True,Ticks->{Range[1,2 r,2],Range[c],Range[10]},ViewPoint->{0,-2,1.5}]

2
Si vous pouvez rendre le chemin, cela gagnera sûrement haut la main! Pouvez-vous modifier la couleur des tuyaux, selon que les sommets sont «touchés»?
jimbobmcgee

1
Je pense que je peux modifier la couleur du tuyau pour montrer le chemin emprunté. Je ne sais pas comment décider quels tuyaux de raccordement (horizontaux) doivent être "pris" et lesquels doivent être évités. Actuellement, tous les tuyaux de raccordement sont "ouverts".
DavidC

Choisissez-en un au hasard, ou rendez-le définissable comme je l'ai fait dans le mien? Va encore gagner - la sortie est trop jolie pour ne pas le faire !!
jimbobmcgee

1
En ce moment, vous semblez prendre plusieurs tuyaux horizontaux au même niveau. Je crois que cela viole la règle de base n ° 1.
Timwi

Timwi, merci. Cette omission a été corrigée.
DavidC

12

C #

(via LINQPad, en mode "Programme C #";)

Il va falloir appliquer Equitable Stroke Control, dans la mesure où tout golf peut aller, mais c'est mon approche en C # (enfin, LINQPad, mais qui veut tout le passe-partout nécessaire pour faire fonctionner une application C # complète?) .

Les définitions de grille sont variables, avec un certain nombre de tuyaux verticaux et la hauteur de la structure globale, et sont répétitivement aléatoires en passant une graine (voir le PipeGridconstructeur).

En l'absence d'une réponse définitive quant à la manière dont l'objet se déplacerait si l'une ou l'autre direction était possible, je vous ai permis de spécifier un comportement à partir d'un certain nombre d'options (voir SolveBehaviorénumération / PipeSolverconstructeur).

Le départ vertical est définissable (voir PipeSolver.Solve).

J'ai supposé que les tuyaux horizontaux sont toujours entre deux tuyaux verticaux adjacents , c'est-à-dire qu'aucun horizontal ne peut contourner un tuyau horizontal.

///<summary>Entry point</summary>
void Main()
{
    var grid = new PipeGrid(vertical:10, height:10, seed:5);
    var solver = new PipeSolver(grid, SolveBehavior.FlipFlop);
    solver.Solve(start:2);
}

///<summary>Represents the direction the object is travelling</summary>
enum Direction
{
    Down = 0,
    Left = 1,
    Right = 2
}

///<summary>Determines the route to take if a junction yields both horizontal directions</summary>
enum SolveBehavior
{
    ///<summary>Throws an <see cref="InvalidOperationException" /></summary>
    Fail = 0,

    ///<summary>Prefers the left-most direction (screen-relative)</summary>
    FavorLeft = 1,

    ///<summary>Prefers the right-most direction (screen-relative)</summary>
    FavorRight = 2,

    ///<summary>Alternates preferred direction, based on the number of turns</summary>
    FlipFlop = 3,

    ///<summary>Prefers the same direction the object travelled, on its last horizontal movement</summary>
    SameDirection = 4,

    ///<summary>Prefers the opposite direction the object travelled, on its last horizontal movement</summary>
    Uturn = 5
}

///<summary>Provides the logic for solving a <see cref="PipeGrid" /></summmary>
class PipeSolver
{
    ///<summary>Creates a new <see cref="PipeSolver" /> for the supplied <paramref name="grid" />,
    ///with the given <paramref name="behavior" /> used to resolve junctions with both horizontal 
    ///paths</summary>
    public PipeSolver(PipeGrid grid, SolveBehavior behavior = SolveBehavior.FlipFlop)
    {       
        if (grid == null) throw new ArgumentNullException("grid");
        _grid = grid;
        _behavior = behavior;
    }

    private readonly PipeGrid _grid;
    private readonly SolveBehavior _behavior;

    ///<summary>Simulate the dropping of an object to run through the grid, at the top of a
    ///given <paramref name="start" /> vertical pipe</summary>
    public void Solve(int start = 1, bool dumpFrames = false, string tag = "Result")
    {
        if (start < 1) start = 1;
        if (start > _grid.Verticals) start = _grid.Verticals;

        int x, y;

        Direction?[,] path = new Direction?[_grid.Width, _grid.Height];

        x = (start - 1) * 2;
        y = 0;
        Direction dir = Direction.Down, lastDir = Direction.Down;

        int turns = 0;      
        do
        {
            path[x, y] = dir;       // we moved through this pipe

            // rule 1: when moving through horizontal pipe, object will go down when possible
            if ((dir == Direction.Left || dir == Direction.Right) && (x % 2 == 0))
            {
                lastDir = dir;
                dir = Direction.Down;
                ++turns;
            }
            // rule 2: when moving through start pipe, object will turn into horizontal pipe when possible
            else if (dir == Direction.Down)
            {
                bool hasLeft  = (x > 0 && _grid[x - 1, y]);
                bool hasRight = (x < _grid.Width - 1 && _grid[x + 1, y]);

                if (hasLeft && hasRight)
                {
                    switch (_behavior)
                    {
                        case SolveBehavior.FavorLeft: 
                            hasRight = false;       // "forget" about right pipe
                            break;
                        case SolveBehavior.FavorRight:
                            hasLeft = false;        // "forget" about left pipe
                            break;
                        case SolveBehavior.FlipFlop:
                            if (turns % 2 == 0) hasLeft = false;
                            else hasRight = false;  // "forget" about left on the even moves, or right on the odd moves
                            break;
                        case SolveBehavior.SameDirection:   // force staying in the same direction
                            if (lastDir == Direction.Left)       hasRight = false;
                            else if (lastDir == Direction.Right) hasLeft = false;
                            else goto case SolveBehavior.FlipFlop;  // use the flip-flop behaviour to determine first turn
                            break;
                        case SolveBehavior.Uturn:   // force turning back on itself
                            if (lastDir == Direction.Left)       hasLeft = false;
                            else if (lastDir == Direction.Right) hasRight = false;
                            else goto case SolveBehavior.FlipFlop;  // use the flip-flop behaviour to determine first turn
                            break;
                        default: throw new InvalidOperationException(
                            "Failed to find distinct path, with no resolving behavior defined"
                        );
                    }
                }

                if (hasLeft)        dir = Direction.Left;
                else if (hasRight)  dir = Direction.Right;

                if (hasLeft || hasRight) ++turns;
            }

            switch (dir)    // update position, based on current direction
            {
                case Direction.Left:  if (x > 0) --x; break;
                case Direction.Right: if (x < _grid.Width - 1) ++x; break;
                default: ++y; break;
            }
            if (dumpFrames) 
            {
                DumpFrame(path, start, tag:string.Concat("Frame #", turns, " (", _grid.Seed, ")"));
                DrawFrame(path, start, tag:string.Concat("Frame #", turns));
            }
        } 
        while (y < _grid.Height);

        int end = (x / 2) + 1;
        DumpFrame(path, start, end, turns, tag);
        DrawFrame(path, start, end, turns, tag);
    }

    ///<summary>Internal method for drawing a given frame</summary>
    private void DumpFrame(Direction?[,] path, int start, int? end = null, int? turns = null, string tag = null)
    {
        var builder = new StringBuilder();

        builder.Append(' ', --start * 5).AppendLine("v");
        for (int y = 0; y < _grid.Height; y++)
        {
            for (int x = 0; x < _grid.Width; x++)
            {
                builder.Append(
                    (x % 2 == 0) 
                        ? path[x, y].HasValue ? ":"    : _grid[x, y] ? "|"    : " "
                        : path[x, y].HasValue ? "====" : _grid[x, y] ? "----" : "    "
                );
            }
            builder.AppendLine();
        }
        if (end.HasValue)   builder.Append(' ', (end.Value - 1) * 5).AppendLine("^");

        if (turns.HasValue) builder.Append(turns.Value)
                                   .Append(" turns were taken to get to ")
                                   .AppendLine(end.HasValue ? "the end." : "this point.");

        builder.ToString().Dump(string.IsNullOrWhiteSpace(tag) ? "Frame" : tag);
    }

    ///<summary>Internal method for rendering a frame as a bitmap</summary>
    private void DrawFrame(Direction?[,] path, int start, int? end = null, int? turns = null, string tag = null)
    {
        using (var sprites = new Sprites())
        using (var canvas = new Bitmap(16 * _grid.Width, 16 * (_grid.Height + 3)))
        using (var graphics = Graphics.FromImage(canvas))
        {
            graphics.FillRectangle(Brushes.Green, 0, 16, 16 * _grid.Width, 16 * _grid.Height);
            _grid.Draw(graphics, sprites, offsetX:0, offsetY:16);

            // draw the start position
            start = (start - 1) * 32;
            graphics.DrawImageUnscaled(sprites.RoadVertical, start, 0);
            graphics.DrawImageUnscaled(sprites.CarVertical,  start, 0);
            graphics.DrawImageUnscaled(sprites.StartFlag,    start, 0);

            // draw the path
            for (int y = 0; y < _grid.Height; y++)
            for (int x = 0; x < _grid.Width;  x++)
            {
                if (path[x, y].HasValue)
                {
                    Image car;

                    switch (path[x, y])
                    {
                        case Direction.Left:
                            // if even, then on a vertical, so turning left; otherwise travelling left
                            car = (x % 2 == 0) ? sprites.CarTurnLeft : sprites.CarLeft;
                            break;
                        case Direction.Right:
                            // if even, then on a vertical, so turning right; otherwise travelling right
                            car = (x % 2 == 0) ? sprites.CarTurnRight: sprites.CarRight;
                            break;
                        default:
                            car = sprites.CarVertical;
                            if (x == 0 && path[x + 1, y].HasValue)                            // far-left and will move right = turn-right
                                car = sprites.CarTurnRight;
                            else if (x == _grid.Width - 1 && path[x - 1, y].HasValue)         // far-right and will move left = turn-left
                                car = sprites.CarTurnLeft;
                            else if (x > 0 && x < _grid.Width - 1)
                            {
                                car = sprites.CarVertical;                                    // if not right or left, then down
                                if (path[x + 1, y].HasValue && !path[x - 1, y].HasValue)      // if came from the left, then turn right
                                    car = sprites.CarTurnRight;
                                else if (path[x - 1, y].HasValue && !path[x + 1, y].HasValue) // if came from the right, then turn left
                                    car = sprites.CarTurnLeft;
                            }
                            break;
                    }

                    graphics.DrawImageUnscaled(car, 16 * x, 16 * (y + 1));
                }
            }

            // draw the end position, if we are at the end
            if (end.HasValue)
            {
                end = (end - 1) * 32;
                graphics.DrawImageUnscaled(sprites.RoadVertical, end.Value, 16 * (_grid.Height + 1));
                graphics.DrawImageUnscaled(sprites.CarVertical,  end.Value, 16 * (_grid.Height + 1));
                graphics.DrawImageUnscaled(sprites.EndFlag,      end.Value, 16 * (_grid.Height + 1));
            }

            if (turns.HasValue) 
            {
                string s = string.Concat(turns.Value, " turns were taken to get to ", 
                                         end.HasValue ? "the end." : "this point.");

                graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
                graphics.DrawString(s, SystemFonts.DefaultFont, Brushes.Black, 0, 16 * (_grid.Height + 2));
            }

            canvas.Dump(tag ?? "Bonus");
        }       
    }
}

///<summary>Represents a configuration of pipes</summary>
class PipeGrid
{
    ///<summary>Creates a new <see cref="PipeGrid" />, of a given <paramref name="height" />
    ///with the given number of <paramref name="vertical" /> pipes, and randomly distributes 
    ///horizontal pipes between them, based on a repeatable <paramref name="seed" />.</summary>
    public PipeGrid(int vertical = 4, int height = 8, int? seed = null)
    {
        if (vertical < 2) vertical = 2;
        if (height < 2) height = 2;

        Width = (2 * vertical) - 1;
        Height = height;
        Verticals = vertical;

        Seed = seed ?? Environment.TickCount;
        var rnd = new Random(Seed);

        _nodes = new bool[Width,Height];
        for (int x = 0, xw = Width; x < xw; x++) 
        for (int y = 0; y < height; y++)
        {
            // place verticals in every even column, and randomly place horizontals in odd columns
            if (x % 2 == 0 || rnd.Next(0, 2) == 1)
                _nodes[x, y] = true;
        }
    }

    private readonly bool[,] _nodes;

    public int Width { get; private set; }
    public int Height { get; private set; }
    public int Verticals { get; private set; }
    public int Seed { get; private set; }

    public bool this[int x, int y] { get { return _nodes[x, y]; } }

    ///<summary>Renders the grid to the LINQPad results pane, for inspection</summary>
    public PipeGrid Dump(string tag = null)
    {
        var builder = new StringBuilder();

        for (int y = 0; y < Height; y++)
        {
            for (int x = 0; x < Width; x++)
            {
                builder.Append(
                    (x % 2 == 0)
                        ? _nodes[x, y] ? "|"    : " "
                        : _nodes[x, y] ? "----" : "    "
                );
            }
            builder.AppendLine();
        }

        builder.ToString().Dump(string.IsNullOrWhiteSpace(tag) ? "Grid" : tag);
        return this;
    }

    ///<summary>Render the grid as a bitmap image</summary>
    public void Draw(Graphics g, Sprites s, int offsetX = 0, int offsetY = 0)
    {           
        for (int y = 0; y < Height; y++)
        {
            for (int x = 0; x < Width; x++)
            {
                if (_nodes[x, y])
                {   
                    Image sprite = sprite = s.RoadVertical;

                    if (x % 2 != 0) 
                        sprite = s.RoadHorizontal;
                    else if (x == 0 && _nodes[1, y])
                        sprite = s.JunctionTeeRight;
                    else if (x == Width - 1 && _nodes[x - 1, y])
                        sprite = s.JunctionTeeLeft;
                    else if (x > 0 && x < Width - 1)
                    {
                        if (_nodes[x - 1, y] && _nodes[x + 1, y])
                            sprite = s.JunctionCross;
                        else if (_nodes[x + 1, y] && !_nodes[x - 1, y])
                            sprite = s.JunctionTeeRight;
                        else if (_nodes[x - 1, y] && !_nodes[x + 1, y])
                            sprite = s.JunctionTeeLeft;
                    }

                    g.DrawImageUnscaled(sprite, 
                                        x:(16 * x) + offsetX, 
                                        y:(16 * y) + offsetY);
                }
            }
        }
    }

    ///<summary>Creates a <see cref="PipeGrid" /> with horizontal pipes at all possible positions</summary>
    public static PipeGrid CreateAllOpen(int verticals = 4, int height = 8)
    {
        var grid = new PipeGrid(verticals, height, 0);
        for (int y = 0; y < height; y++)
        for (int x = 0, xw = grid.Width; x < xw; x++)
            grid._nodes[x, y] = true;

        return grid;
    }
}

///<summary>Store tile sprites, to be used in the graphical rendering of the result</summary>
class Sprites : IDisposable
{
    public Sprites()
    {
        byte[,] car = new byte[,] {
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0 },
            { 0, 0, 0, 1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0 },
            { 0, 0, 0, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0, 0, 0 },
            { 0, 0, 0, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0, 0, 0 },
            { 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 3, 1, 1, 1, 1, 1, 1, 3, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 3, 1, 1, 1, 1, 1, 1, 3, 0, 0, 0, 0 },
            { 0, 0, 0, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0, 0, 0 },
            { 0, 0, 0, 1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0 },
            { 0, 0, 0, 1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0 },
            { 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0 },
            { 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        };
        byte[,] road = new byte[,] {
            { 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
        };
        byte[,] roadNESW = new byte[,] {
            { 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
            { 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6 },
            { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
            { 2, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
            { 2, 2, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
            { 2, 2, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
            { 2, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
            { 2, 2, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
            { 2, 2, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
            { 2, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
            { 2, 2, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
            { 2, 2, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
            { 2, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
            { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
            { 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6 },
            { 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
        };
        byte[,] roadNES = new byte[,] {
            { 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
            { 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6 },
            { 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
            { 0, 6, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
            { 0, 6, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
            { 0, 6, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
            { 0, 6, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
            { 0, 6, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
            { 0, 6, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
            { 0, 6, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
            { 0, 6, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
            { 0, 6, 2, 2, 5, 5, 2, 5, 5, 2, 5, 5, 2, 2, 2, 2 },
            { 0, 6, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 5, 2, 2, 2 },
            { 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
            { 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6 },
            { 0, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 0 },
        };
        byte[,] start = new byte[,] {
            { 0, 0, 1, 1, 1, 0, 4, 4, 4, 0, 0, 0, 4, 0, 0, 0 },
            { 0, 0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0 },
            { 0, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0 },
            { 0, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0 },
            { 0, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0 },
            { 0, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0 },
            { 0, 0, 1, 4, 4, 4, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        };
        byte[,] end = new byte[,] {
            { 0, 0, 1, 1, 1, 0, 1, 1, 6, 0, 0, 0, 6, 0, 0, 0 },
            { 0, 0, 1, 6, 6, 6, 1, 1, 6, 6, 1, 1, 6, 0, 0, 0 },
            { 0, 0, 1, 1, 6, 6, 6, 6, 1, 6, 1, 1, 1, 0, 0, 0 },
            { 0, 0, 1, 1, 1, 1, 6, 6, 1, 1, 6, 6, 1, 0, 0, 0 },
            { 0, 0, 1, 1, 1, 1, 1, 1, 6, 1, 6, 6, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 6, 6, 1, 1, 6, 6, 1, 1, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 6, 6, 6, 6, 1, 6, 1, 1, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 1, 1, 6, 6, 1, 1, 6, 6, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 6, 6, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        };

        RoadVertical     = Sprite(road);
        RoadHorizontal   = RotateSprite(RoadVertical, 90);
        JunctionCross    = Sprite(roadNESW);
        JunctionTeeRight = Sprite(roadNES);
        JunctionTeeLeft  = FlipSprite(JunctionTeeRight, horizontal:true);
        CarVertical      = Sprite(car);
        CarLeft          = RotateSprite(CarVertical,  90);
        CarRight         = FlipSprite(CarLeft, horizontal:true);
        CarTurnLeft      = RotateSprite(CarVertical,  45);
        CarTurnRight     = FlipSprite(CarTurnLeft, horizontal:true);
        StartFlag        = Sprite(start);
        EndFlag          = Sprite(end);
    }

    public Image RoadVertical     { get; private set; }
    public Image RoadHorizontal   { get; private set; }
    public Image JunctionCross    { get; private set; }
    public Image JunctionTeeLeft  { get; private set; }
    public Image JunctionTeeRight { get; private set; }
    public Image CarVertical      { get; private set; }
    public Image CarLeft          { get; private set; }
    public Image CarRight         { get; private set; }
    public Image CarTurnLeft      { get; private set; }
    public Image CarTurnRight     { get; private set; }
    public Image StartFlag        { get; private set; }
    public Image EndFlag          { get; private set; }

    ///<summary>Create a sprite from the byte data</summary>
    private Image Sprite(byte[,] data) 
    {
        int width = data.GetLength(0);
        int height = data.GetLength(1);

        var image = new Bitmap(width, height);

        for (int y = 0; y < height; y++)
        for (int x = 0; x < width; x++)
        {
            Color c;
            switch (data[y,x])
            {
                case 1: c = Color.Black; break;
                case 2: c = Color.DarkGray; break;
                case 3: c = Color.Red; break;
                case 4: c = Color.LimeGreen; break;
                case 5: c = Color.Yellow; break;
                case 6: c = Color.White; break;
                default: continue;
            }

            image.SetPixel(x, y, c);
        }

        return image;
    }

    ///<summary>Rotate an image by a number of <paramref name="degrees" /> around the centre</summary>
    private Image RotateSprite(Image source, float deg)
    {
        var b = new Bitmap(source.Width, source.Height);

        using (var g = Graphics.FromImage(b))
        {
            float tx = (float)source.Width / 2.0f;
            float ty = (float)source.Height / 2.0f;

            g.TranslateTransform(tx, ty);
            g.RotateTransform(deg);
            g.TranslateTransform(-tx, -ty);
            g.DrawImageUnscaled(source, 0, 0);
        }

        return b;
    }

    ///<summary>Flip an image about its centre</summary>
    private Image FlipSprite(Image source, bool horizontal = false, bool vertical = false)
    {
        var b = new Bitmap(source);

        RotateFlipType rft = ( horizontal &&  vertical) ? RotateFlipType.RotateNoneFlipXY
                           : ( horizontal && !vertical) ? RotateFlipType.RotateNoneFlipX
                           : (!horizontal &&  vertical) ? RotateFlipType.RotateNoneFlipY
                           : RotateFlipType.RotateNoneFlipNone;

        b.RotateFlip(rft);
        return b;
    }

    #region IDisposable implementation
    public void Dispose() { Dispose(true); }
    ~Sprites() { Dispose(false); }
    protected void Dispose(bool disposing)
    {
        if (disposing)
        {
            GC.SuppressFinalize(this);
            using (RoadVertical) { }    
            using (RoadHorizontal) { }
            using (JunctionCross) { }
            using (JunctionTeeLeft) { }
            using (JunctionTeeRight) { }
            using (CarVertical) { }
            using (CarLeft) { }
            using (CarRight) { }
            using (CarTurnLeft) { }
            using (CarTurnRight) { }
            using (StartFlag) { }
            using (EndFlag) { };
        }
        RoadVertical = null;
        RoadHorizontal = null;
        JunctionCross = null;
        JunctionTeeLeft = null;
        JunctionTeeRight = null;
        CarVertical = null;
        CarLeft = null;
        CarRight = null;
        CarTurnLeft = null;
        CarTurnRight = null;
        StartFlag = null;
        EndFlag = null;
    }
    #endregion
}

Mise à jour:

Craignant que mon ancienne sortie de texte brut puisse être un peu terne pour ce contexte de popularité, je propose une version étendue qui trace également le chemin emprunté en tant qu'image. Je l'ai modélisé comme une voiture, traversant un horrible réseau routier, essayant d'aller au fond, mais avec le pire GPS du monde qui vous oblige à faire un virage à chaque intersection.

En prime, cela permet également de voir plus clairement les «virages».

Profitez - vroom vroom !!

Exemples de résultats:

(verticales: 10, hauteur: 10, graine aléatoire: 5, pipe de départ: 2, comportement de résolution: FlipFlop}) Exemples de résultats, comme vu dans la page de résultats LINQPad, pour {vertical: 10, hauteur: 10, graine: 5, début: 2, comportement: FlipFlop}


8

C #

Des moments de plaisir! :-)

Ce code garantit qu'il y a au moins un tuyau droit à chaque extrémité de chaque tuyau. Il garantit également qu'il n'y a pas de virages ambigus.

using System;

namespace Experiment.DownTheDrain
{
    class Program
    {
        static void Main(string[] args)
        {
            var program = new Program();
            program.Width = 19;
            program.Height = 17;
            program.SpanCount = program.Width * program.Height / 4;
            program.Spacing = 3;
            program.Run();
        }

        public int Width { get; set; }
        public int Height { get; set; }
        public int SpanCount { get; set; }
        public int Spacing { get; set; }
        public bool[,] Spans { get; private set; }

        public void Run()
        {
            GenerateSpans();
            DrawPipes();
            var pipe = ReadStartPipe();
            var turns = DrawPath(pipe);
            WriteTurns(turns);
            Console.ReadLine();
        }

        private void GenerateSpans()
        {
            Random random = new Random();
            Spans = new bool[Width, Height];

            int x, y;
            for (int i = 0; i < SpanCount; i++)
            {
                do
                {
                    x = random.Next(Width);
                    y = random.Next(Height);
                }
                while (SpanAt(x - 1, y) || SpanAt(x, y) || SpanAt(x + 1, y));
                Spans[x, y] = true;
            }
        }

        private void DrawPipes()
        {
            const string Junction = "│┤├┼";

            Console.CursorLeft = 0;
            Console.CursorTop = 0;
            DrawLabels();
            for (int y = -1; y <= Height; y++)
            {
                for (int x = 0; x <= Width; x++)
                {
                    Console.Write(Junction[(SpanAt(x-1,y) ? 1 : 0) + (SpanAt(x,y) ? 2 : 0)]);
                    Console.Write(x == Width ? Environment.NewLine : new string(SpanAt(x, y) ? '─' : ' ', Spacing));
                }
            }
            DrawLabels();
        }

        private void DrawLabels()
        {
            for (int x = 0; x <= Width; x++)
                Console.Write("{0}{1}",
                    (char)(x + 65),
                    x == Width ? Environment.NewLine : new string(' ', Spacing)
                );
        }

        private int ReadStartPipe()
        {
            Console.WriteLine();
            Console.Write("Please select a start pipe: ");
            int pipe;
            do
            {
                var key = Console.ReadKey(true);
                pipe = (int)char.ToUpper(key.KeyChar) - 65;
            }
            while (pipe < 0 || pipe > Width);
            Console.WriteLine((char)(pipe + 65));
            return pipe;
        }

        private int DrawPath(int x)
        {
            int turns = 0;
            Console.CursorTop = 1;
            for (int y = -1; y <= Height; y++)
            {
                if (SpanAt(x - 1, y))
                {
                    x--;
                    Console.CursorLeft = x * (Spacing + 1);
                    Console.WriteLine("╔{0}╝", new string('═', Spacing));
                    turns += 2;
                }
                else if (SpanAt(x, y))
                {
                    Console.CursorLeft = x * (Spacing + 1);
                    Console.WriteLine("╚{0}╗", new string('═', Spacing));
                    x++;
                    turns += 2;
                }
                else
                {
                    Console.CursorLeft = x * (Spacing + 1);
                    Console.WriteLine("║");
                }
            }

            return turns;
        }

        private void WriteTurns(int turns)
        {
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("{0} turns taken to reach the bottom.", turns);
        }

        private bool SpanAt(int x, int y)
        {
            return x >= 0
                && x < Width
                && y >= 0
                && y < Height
                && Spans[x, y];
        }
    }
}

Production:

Dans l'évier


1
Merde, j'ai aussi eu l'idée d'utiliser les personnages de dessin de boîte, tu m'as battu :)
Timwi

De plus, j'aime l'idée de ne pas générer de tuyaux avec des virages ambigus. Je n'y ai pas pensé!
Timwi

8

Funciton

Comme si Funciton n'était pas déjà parmi les langues les plus inutiles au monde, c'est certainement le programme le plus inutile que j'ai écrit jusqu'à présent.

Étant donné que cela semble moche dans StackExchange en raison de l'espacement de ligne supplémentaire, envisagez d'exécuter ce qui suit dans la console JavaScript de votre navigateur pour résoudre ce problème:

$('pre').each(function(){$(this).css('line-height',1)})

Étant donné que Funciton n'a pas de générateur de nombres aléatoires, j'ai décidé de vous laisser entrer le modèle de tuyau. Étant donné que l'encodage du motif n'est pas évident, frapper les touches numériques de votre clavier au hasard est aussi bon qu'un générateur de nombres aléatoires.

L'entrée devrait être composée de trois nombres décimaux séparés par des espaces. Le premier nombre est la largeur (un de moins que le nombre de tuyaux verticaux); le second est l'indice du tuyau de départ et le dernier est un nombre quelconque qui code le modèle de tuyau horizontal; vous pouvez le rendre aussi grand que vous le souhaitez. Si la largeur est négative ou que l'indice du tuyau est hors limites, la sortie estImpossiburu.

Le programme s'assure automatiquement qu'il n'y a jamais deux tuyaux horizontaux l'un à côté de l'autre, ce qui pourrait provoquer des virages ambigus.

                            ┌───╖
               ┌────────────┤ ♯ ╟───────────┬───────────────┐
     ╔════╗  ┌─┴─╖  ┌────╖  ╘═══╝  ╔═══╗  ┌─┴─╖  ╔════╗     │
     ║ 21 ║  │ × ╟──┤ >> ╟─────────╢   ╟──┤ ʘ ╟──╢ 32 ║     │
     ╚═╤══╝  ╘═╤═╝  ╘═╤══╝         ╚═══╝  ╘═══╝  ╚════╝     │
       └───────┘  ┌───┴───┐                                 │
   ╔════╗  ┌───╖  │   ┌───┴──────────────────┐              │
   ║ 32 ╟──┤ ʘ ╟──┘   │          ╔═══╗       │              │
   ╚════╝  ╘═╤═╝    ┌─┴─╖        ║ 0 ╟───┐   │              │
     ┌───────┴──────┤ · ╟────┐   ╚═══╝ ┌─┴─╖ │              │
     │              ╘═╤═╝    └─────────┤ ʃ ╟─┘              │
     │     ┌──────────┘                ╘═╤═╝                │
   ┌─┴─╖ ┌─┴──╖  ┌─────────╖        ┌────┴────╖             │
   │ ♯ ║ │ >> ╟──┤ str→int ╟────┐   │ str→int ║             │
   ╘═╤═╝ ╘═╤══╝  ╘═════════╝  ┌─┴─╖ ╘════╤════╝           ┌─┴─╖
     │   ┌─┴─╖ ╔════╗         │ ░ ║   ┌──┴────────────────┤ · ╟────────────┐
     └───┤ × ╟─╢ 21 ║         ╘═╤═╝ ┌─┴─╖                 ╘═╤═╝            │
         ╘═══╝ ╚════╝┌─────┐    └───┤ ▒ ╟───┐ ┌─────────╖ ┌─┴─╖            │
      ┌─────────╖  ┌─┴─╖   │        ╘═╤═╝   ├─┤ str→int ╟─┤ ʃ ╟─┐          │
   ┌──┤ int→str ╟──┤ · ╟─┐ └──────────┘     │ ╘═════════╝ ╘═╤═╝ │          │
   │  ╘═════════╝  ╘═╤═╝ │ ╔══════════════╗ │ ╔═══╗  ┌──────┘   │          │
   │ ╔══════════╗  ┌─┴─╖ │ ║ 158740358500 ║ │ ║   ╟──┘ ┌───╖  ╔═╧═╗  ┌───╖ │
   │ ║ 20971533 ╟──┤   ╟─┘ ║ 305622435610 ║ │ ╚═══╝  ┌─┤ ≤ ╟──╢ 0 ╟──┤ ≥ ╟─┴─┐
   │ ╚════╤═════╝  └─┬─╜   ║ 491689778976 ║ └────────┤ ╘═╤═╝  ╚═══╝  ╘═╤═╝   │
   │    ┌─┴─╖  ┌───╖ │     ║ 886507240727 ║          │   └──────┬──────┘     │
   │    │ ‼ ╟──┤ ‼ ╟─┘     ║ 896192890374 ║          │         ┌┴┐           │
   │    ╘═╤═╝  ╘═╤═╝       ║ 899130957897 ╟───┐      │         └┬┘           │
   └──────┘    ┌─┴─╖       ╚══════════════╝ ┌─┴─╖  ┌─┴─╖        │            │
               │ ‼ ╟────────────────────────┤ ? ╟──┤ · ╟────────┤            │
               ╘═╤═╝                        ╘═╤═╝  ╘═╤═╝      ┌─┴─╖          │
 ╔═══════════════╧════════════════════════╗   │      └────────┤ ≥ ╟──────────┘
 ║ 83139057126481738391428729850811584337 ║       ┌────╖      ╘═══╝
 ║ 75842912478026089564602018574355013746 ║  ┌────┤ >> ╟──┐
 ║ 85373033606532129933858395805642598753 ║  │    ╘══╤═╝  │
 ║ 19381927245726769973108298347355345088 ║  │     ┌─┴─╖  │       ╓───╖
 ║ 84932603219463911206052446527634696060 ║  │     │ ░ ║  │       ║ ░ ║
 ║ 230797436494578049782495796264992      ║  │     ╘═╤═╝  │       ╙─┬─╜
 ╚════════════════════════════════════════╝  │    ┌──┴──┐ └─────────┴────────┐
                                             │    │   ┌─┴──╖ ┌┐   ┌┐         │
                                             │    │   │ << ╟─┤├─┬─┤├─────┐   │
   ┌────╖  ╔═══╗                             │    │   ╘═╤══╝ └┘ │ └┘   ╔═╧═╗ │
 ┌─┤ << ╟──╢ 1 ║                             │    │   ╔═╧═╗     │      ║ 1 ║ │
 │ ╘═╤══╝  ╚═══╝      ┌───────────────────┐  │    │   ║ 2 ║     │      ╚═╤═╝ │
 │   └─────┬──────────┴─────────┐         │  │ ┌──┴─╖ ╚═══╝   ┌─┴─╖ ┌┐   │   │
 │         │           ┌───┐  ┌─┴─╖       │  │ │ << ╟─────────┤ ? ╟─┤├───┤   │
 │         │           │   ├──┤ · ╟─┐     │  │ ╘══╤═╝         ╘═╤═╝ └┘   ├───┘
 │ ┌───┐ ┌─┴─╖ ┌───╖   └─┬─┘  ╘═╤═╝ ├───┐ │  │  ╔═╧═╗  ╔═══╗  ┌─┴─╖      │
 ├─┤   ├─┤ · ╟─┤ ♯ ╟─────┘    ┌─┴─╖ │   │ │  └──╢ 1 ║  ║ 0 ╟──┤ ? ╟──────┘
 │ └───┘ ╘═╤═╝ ╘═══╝  ┌───────┤ · ╟─┴─┐ │ │     ╚═══╝  ╚═══╝  ╘═╤═╝
 │       ┌─┴─╖      ┌─┴─╖     ╘═╤═╝   │ │ └─────────────────┐   │
 │  ┌────┤ · ╟──────┤ · ╟──┐    │     │ └────────┐          │
 │  │    ╘═╤═╝      ╘═╤═╝  │    │     └────┐     │          │
 │  └───┬──┴──┐       │    │    │        ┌─┴──╖  │          │
 │      │    ┌┴┐      │    │    └─────┬──┤ << ║  │          │
 │      │    └┬┘      │    │         ┌┴┐ ╘═╤══╝  │          │
 │    ┌─┴─╖ ┌─┴─╖   ┌─┴─╖  │         └┬┘ ╔═╧═╗   │          │
 └────┤ · ╟─┤ ? ╟─┐ │ ♯ ║  ├──────────┤  ║ 1 ║   │          │
      ╘═╤═╝ ╘═╤═╝ │ ╘═╤═╝ ┌┴┐         │  ╚═══╝   │          │
        │     │   │ ┌─┴─╖ └┬┘         │          │          │
        │     │   └─┤ ? ╟──┘        ┌─┴─╖        │          │
        │     │     ╘═╤═╝     ┌─────┤ > ╟─────┬──┘          │
        │     │     ┌─┴─╖   ┌─┴─╖   ╘═══╝     ├─────────┐   │
      ┌─┴─╖   └─────┤ · ╟───┤ · ╟─────────────┘         │   │
    ┌─┤ · ╟─────┐   ╘═╤═╝   ╘═╤═╝                       │   │
    │ ╘═╤═╝  ┌──┴─╖ ┌─┴─╖     │                         │   │
    │   │    │ >> ╟─┤ ▒ ╟──┐  ├─────────────┐         ┌─┴─╖ │
    │   │    ╘══╤═╝ ╘═╤═╝  ├──┘             │ ╓───╖ ┌─┤ · ╟─┤
    │   │       │   ┌─┴─╖  │                ├─╢ ▒ ╟─┤ ╘═╤═╝ │
    │   │       └───┤ · ╟──┘                │ ╙─┬─╜ │   │   │
    │   │           ╘═╤═╝                   │   │ ┌─┴─╖ │   │
    │   │           ┌─┴─╖                   │   └─┤ · ╟─┤   │
    │   │        ┌──┤   ╟────────────┐      │     ╘═╤═╝ │   │
    │   │        │  └─┬─╜  ┌───╖   ┌─┴─╖  ┌─┴─╖   ┌─┴─╖ │   │
    │   └──────┐ │    └────┤ ‼ ╟───┤ · ╟──┤ · ╟───┤ ▓ ╟─┘   │
    │          │ │         ╘═╤═╝   ╘═╤═╝  ╘═╤═╝   ╘═╤═╝     │
    │          │ │  ╔═══╗  ┌─┴─╖     │      └───────┘       │
    │          │ └──╢ 0 ╟──┤ ? ╟─┐   │                      │
    │          │    ╚═══╝  ╘═╤═╝ │ ┌─┴─╖                    │
    │ ╔═══╗    │           ┌─┴─╖ ├─┤ · ╟─┐                  │
    │ ║ 2 ║    │      ┌────┤ · ╟─┘ ╘═╤═╝ ├──────────────────┘
    │ ╚═╤═╝    │      │    ╘═╤═╝     │   │
    │ ┌─┴─╖  ┌─┴─╖  ┌─┴─╖  ╔═╧═╕ ┌─┐ │   │
    │ │ + ╟──┤ ? ╟──┤ ? ╟──╢   ├─┴─┘ │   │
    │ ╘═╤═╝  ╘═╤═╝  ╘═╤═╝  ╚═╤═╛     │   │
    │   └──┬───┘    ╔═╧═╗    │       │   │
    │      │        ║ 0 ║            │   │
    │      │        ╚═══╝            │   │
    │      └─────────────────────────┘   │
    └────────────────────────────────────┘        ╓┬──╖
 ┌────────────────────────────────────────────────╫┘▓ ╟────────────┐
 │ ╔═══════════╗        ╔════════════════════╗    ╙─┬─╜            │
 │ ║ 387759291 ║        ║ 385690484238253342 ║      │              │
 │ ║ 565251600 ║    ┌───╢ 839653020379129116 ║      │    ┌────┐    │
 │ ║ 199735775 ║  ┌─┴─╖ ╚════════════════════╝      │    │   ┌┴┐   │
 │ ║ 904933210 ╟──┤ ? ╟────────────────┐            │    │   └┬┘   │
 │ ╚═══════════╝  ╘═╤═╝    ┌──────┐    ├────────────┴────┘  ┌─┴─╖  │
 │ ╔═══════════╗  ┌─┴─╖  ┌─┴─╖  ╔═╧═╗  │  ╔════╗    ╔═══╗   │ ♯ ║  │
 │ ║ 388002680 ╟──┤ ? ╟──┤ ≠ ║  ║ 1 ║  │  ║ 21 ║  ┌─╢ 1 ║   ╘═╤═╝  │
 │ ║ 480495420 ║  ╘═╤═╝  ╘═╤═╝  ╚═══╝  │  ╚═╤══╝  │ ╚═══╝    ┌┴┐   │
 │ ║ 244823142 ║    │      ├───────────┘    │     │          └┬┘   │
 │ ║ 920365396 ║    │    ┌─┴─╖  ┌───╖     ┌─┴──╖  │ ┌────╖  ┌─┴─╖  │
 │ ╚═══════════╝    └────┤ · ╟──┤ ‡ ╟─────┤ >> ║  └─┤ >> ╟──┤ · ╟──┴─┐
 │ ╔═══════════╗ ┌───┐   ╘═╤═╝  ╘═╤═╝     ╘═╤══╝    ╘═╤══╝  ╘═╤═╝    │
 │ ║ 618970314 ╟─┘ ┌─┴─╖ ┌─┘      │         │       ┌─┴─╖     │      │
 │ ║ 790736054 ║ ┌─┤ ? ╟─┴─┐      │         ├───────┤ ▓ ╟─────┘      │
 │ ║ 357861634 ║ │ ╘═╤═╝   │      │     ┌───┘       ╘═╤═╝            │
 │ ╚═══════════╝ │ ┌─┴─╖ ┌─┴─╖  ┌─┴─╖ ┌─┴─╖         ┌─┴─╖     ╔═══╗  │
 │ ╔═══════════╗ │ │ ‼ ╟─┤ · ╟──┤ ? ╟─┤ · ╟─────────┤ · ╟──┐  ║ 1 ║  │
 │ ║ 618970314 ╟─┘ ╘═╤═╝ ╘═╤═╝  ╘═╤═╝ ╘═╤═╝         ╘═╤═╝  │  ╚═╤═╝  │
 │ ║ 790736054 ║     │   ┌─┴─╖  ┌─┴─╖   │   ╓┬──╖    ┌┴┐  ┌┴┐   │    │
 │ ║ 357861713 ║     └───┤ · ╟──┤ · ╟───┘ ┌─╫┘‡ ╟─┐  └┬┘  └┬┘   │    │
 │ ╚═══════════╝         ╘═╤═╝  ╘═╤═╝     │ ╙───╜ │ ┌─┴─╖  └────┤    │
 │ ╔════════════════╗    ┌─┴─╖  ┌─┴─╖     │ ┌───╖ │ │ ♯ ║       └────┘
 │ ║ 43980492383490 ╟────┤ ? ╟──┤ ? ╟───┐ └─┤ ‼ ╟─┘ ╘═╤═╝
 │ ╚════════════════╝    ╘═╤═╝  ╘═╤═╝   │   ╘═╤═╝    ┌┴┐
 │ ╔════════════════╗      │      │     │     │      └┬┘
 │ ║ 43980492383569 ╟──────┘            └──────┬──────┘
 │ ╚════════════════╝                          │
 └─────────────────────────────────────────────┘

Explication

  • Le programme principal trouve les deux premiers espaces et répartit les nombres. Il exécute le troisième (le modèle de tuyau) puis appelle avec le résultat, qui renvoie la sortie ainsi que le nombre de tours effectués. Il ajoute ensuite le texte n turns were taken to get to the end., où nest le nombre de tours calculé par .

  • prend un nombre et insère un 0-bit après chaque 1-bit, garantissant ainsi qu'il n'y a jamais deux tuyaux horizontaux à la suite.

  • génère la sortie en appelant successivement puis en décalant le bon nombre de bits du modèle de canal jusqu'à ce qu'il soit nul. Il incrémente ou décrémente également le «tuyau actuel» de manière appropriée.

  • génère une ligne de sortie. À chaque itération, il décale un bit du modèle de tuyau et décide ensuite de produire (+ 4 espaces), (+ 4 espaces) ├────┤, ╔════╝ou ╚════╗; dans les trois derniers cas, il supprime le premier caractère de l'itération suivante. La dernière itération génère │\r\nou ║\r\nselon le cas.

Exemple de sortie

Contribution:

6 3 73497529294753

Production:

├────┤    │    ║    │    │    │
├────┤    │    ╚════╗    ├────┤
│    ├────┤    ╔════╝    ├────┤
│    ├────┤    ║    │    │    │
│    │    │    ║    │    ├────┤
│    │    │    ║    ├────┤    │
│    ├────┤    ╚════╗    ├────┤
│    ├────┤    │    ║    │    │
│    ├────┤    ╔════╝    │    │
├────┤    ╔════╝    │    ├────┤
│    │    ║    │    │    ├────┤

10 turns were taken to get to the end.

Contribution:

3 0 65536

Production:

║    │    │    │
║    │    │    │
║    │    ├────┤

0 turns were taken to get to the end.

Contribution:

3 7 75203587360867 (indice de tuyau hors plage)

Production:

Impossiburu.

3

Groovy, 311

t=System.in.text.collect{it.collect{it}};s=t.size();d=0;c=0;y=0;x=t[0].indexOf('|');println' '*x+'V'
while(y<s){t[y][x]=t[y][x]=='|'?':':'='
if(d){x+=d}else y++
if(y<s)if(d){if(t[y][x]=='|'){d=0;c++}}else if(t[y][x+1]=='-'){d=1;c++} else if(t[y][x-1]=='-'){d=-1;c++}}
t.each{println it.join()}println' '*x+'^'+c
  • ligne 1: configuration et recherche en premier |
  • ligne 2: remplacer le remplacement: ou =
  • ligne 3: déplacer x / y à la position suivante (d = 0 = bas, d = -1 = gauche, d = 1 = droite)
  • ligne 4: trouver la prochaine direction
  • ligne 5: imprimer les résultats

Ici, il est formaté:

t=System.in.text.split('\n').collect{it.collect{it}}; s=t.size()
d=0; c=0; y=0; x=t[0].indexOf('|')
println ' '*x + 'V'
while (y<s) { 
    t[y][x] = t[y][x] == '|' ? ':' : '='
    if (d) {x += d} else y++
    if (y<s) 
        if (d) {if (t[y][x]=='|') {d = 0; c++}} 
        else if(t[y][x+1]=='-') {d = 1; c++}
        else if(t[y][x-1]=='-') {d = -1; c++}
}
t.each {println it.join()}
println ' '*x + '^'+c

Sortie de l'échantillon:

V
:       |       |-------|  
:=======:       |-------|  
|       :=======:       |  
|-------|-------:=======:  
|       |-------|       :  
|-------|       :=======:  
|-------|       :=======:  
|       |-------|       :
                        ^10

Autre sortie:

V
:       |       |-------|       |
:=======:       |-------|       |
|       :=======:       |-------|  
|-------|-------:=======:       |
|       |-------|       :=======:
|-------|       |-------|       :
|-------|       |-------|       :
|       |-------|       :=======:
|       |-------|       :       |
|       |       :=======:       |
|-------|       :       |       |
|       :=======:       |       |
|       :       |-------|       |
|       :=======:       |       |
                ^16

(Je sais que j'ai fait du golf de code au lieu de la popularité, mais c'est juste mon style)


2

Javascript

Le canevas HTML écoute les événements de clic et trouve la ligne de tuyaux la plus proche et l'utilise comme point de départ. La sortie est dessinée sur le canevas et il y a aussi une zone de texte contenant la sortie ASCII définie par l'OP.

L'algorithme garantira qu'il n'y a jamais deux tuyaux horizontaux de connexion. Autre que cette restriction, une probabilité est définie dans les variables initiales et est utilisée pour déterminer aléatoirement si un tuyau horizontal doit se produire ou non.

JSFIDDLE

<html>
<head>
<script type="text/javascript">
var     WIDTH       = 20,
        HEIGHT      = 15,
        JUNCTIONS   = [],
        PROBABILITY = 0.2,
        COLOR1      = '#00DD00',
        COLOR2      = '#DD0000',
        RADIUS      = 4,
        SPACING     = 20,
        turns       = 0,
        pipe        = 10,
        canvas,
        context;

function Junction( x, y ){
    this.x = x;
    this.y = y;
    this.l = null;
    this.r = null;

    if ( y == 0 )
        JUNCTIONS[x] = [];

    JUNCTIONS[x][y] = this;

    var l = this.left();
    if ( x > 0 && l.l == null && Math.random() <= PROBABILITY )
    {
        this.l = l;
        l.r = this;
    }
}

Junction.prototype.left = function(){
    return this.x == 0?null:JUNCTIONS[this.x-1][this.y];
}
Junction.prototype.right= function(){
    return this.x == WIDTH-1?null:JUNCTIONS[this.x+1][this.y];
}
Junction.prototype.down = function(){
    return this.y == HEIGHT-1?null:JUNCTIONS[this.x][this.y+1];
}
Junction.prototype.reset = function(){
    this.entry = null;
    this.exit = null;
}

Junction.prototype.followPipe = function( prev ){
    this.entry = prev;
    if ( prev === this.l || prev === this.r ) {
        this.exit = this.down() || true;
        turns++;
    } else if ( this.l !== null ) {
        this.exit = this.l;
        turns++;
    } else if ( this.r !== null ) {
        this.exit = this.r;
        turns++;
    } else
        this.exit = this.down() || true;
    console.log( this.exit );
    if ( this.exit !== true )
        this.exit.followPipe( this );
}

Junction.prototype.toString = function(){
    if ( this.entry === null ){
        if ( this.r === null )  return '|  ';
                                        return '|--';
    } else {
        if ( this.r === null )  return ':  ';
                                        return ':==';
    }
}

function init(){
    for ( var x = 0; x < WIDTH; ++x )
        for ( var y = 0; y < HEIGHT; ++y )
            new Junction( x, y );

    canvas  = document.getElementById('canvas');
    context = canvas.getContext('2d');

    canvas.addEventListener('click', draw );

    draw();
}

function draw( evt ){
    for ( var x = 0; x < WIDTH; ++x )
        for ( var y = 0; y < HEIGHT; ++y )
            JUNCTIONS[x][y].reset();

    if ( evt ){ 
        pipe = Math.round((evt.clientX - canvas.getBoundingClientRect().left)/SPACING)-1;
        if ( pipe < 0 )     pipe = 0;
        if ( pipe >= WIDTH )    pipe = WIDTH - 1;
    }

    turns = 0;
    JUNCTIONS[pipe][0].followPipe( true );

    context.clearRect(0, 0, canvas.width, canvas.height);
    context.lineWidth = 2;

    for ( var y = 0; y < HEIGHT; ++y ) {
        for ( var x = 0; x < WIDTH; ++x ) {
            var j = JUNCTIONS[x][y];
                    e = j.entry;

            if ( j.r !== null ){
                context.beginPath();
                context.strokeStyle = e===null?COLOR1:COLOR2;
                context.moveTo(SPACING*(x+1), SPACING*(y+1));
                context.lineTo(SPACING*(x+2), SPACING*(y+1));
                context.stroke();
            }

            if ( y > 0 ){
                context.beginPath();
                context.strokeStyle = (e===JUNCTIONS[x][y-1])?COLOR2:COLOR1;
                context.moveTo(SPACING*(x+1), SPACING*(y));
                context.lineTo(SPACING*(x+1), SPACING*(y+1));
                context.stroke();
            }
        }
    }

    for ( var y = 0; y < HEIGHT; ++y ) {
        for ( var x = 0; x < WIDTH; ++x ) {
            context.beginPath();
            context.arc(SPACING*(x+1), SPACING*(y+1), RADIUS, 0, 2*Math.PI, false);
            context.fillStyle = JUNCTIONS[x][y].entry===null?COLOR1:COLOR2;
            context.fill();
        }
    }

    var h = [];
    for ( var x = 0; x < WIDTH; ++x )
        h.push( x!=pipe?'   ':'v  ' );
    h.push( '\n' );
    for ( var y = 0; y < HEIGHT; ++y ) {
        for ( var x = 0; x < WIDTH; ++x )
            h.push( JUNCTIONS[x][y].toString() )
        h.push( '\n' );
    }
    for ( var x = 0; x < WIDTH; ++x )
        h.push( JUNCTIONS[x][HEIGHT-1].exit!==true?'   ':'^  ' );
    h.push( '\n' );
    h.push( turns + ' turns were taken to get to the end.' );
    document.getElementById( 'output' ).value = h.join( '' );
}

window.onload   = init;
</script>
</head>
<body>
<p>Click on a row to show the path</p>
<canvas id="canvas" width="450" height="350"></canvas>
<textarea id="output" style="width:100%; height:24em;"></textarea>
</body>
</html>

Production

http://imageshack.com/a/img600/9241/94wh.png

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.