Lego Gear Train

13

Inspiriert von den Lego-Übersetzungsverhältnissen von Keith Randall.

Auch ich plane den Bau eines riesigen Lego-Roboters, der in der Lage sein wird, die anderen Roboter in dem noch nie zuvor erwähnten Wettbewerb zu zerstören. * Während des Konstruktionsprozesses des Roboters werde ich viele Getriebezüge verwenden, um die Verbindung herzustellen verschiedene Teile des Roboters. Ich möchte, dass Sie mir das kürzeste Programm schreiben, mit dem ich die komplexen Getriebezüge konstruieren kann, die für eine solch komplexe Aufgabe erforderlich sind. Natürlich verwende ich nur Zahnräder mit den Radien 1, 2, 3 und 5 für beliebige Lego-Einheiten.

Jedes Zahnrad im Getriebezug hat eine bestimmte Ganzzahlkoordinate in einem 2D-Gitter. Der erste Gang befindet sich bei (0,0) und der letzte Gang befindet sich bei nicht negativen Koordinaten. Die Position und Größe des ersten und letzten Gangs werden als Eingabe bereitgestellt. Ihr Programm muss angeben, welche Gänge wohin geschaltet werden, um die Lücken zu füllen.

Zusätzlich muss Ihr Programm die minimal mögliche Anzahl von Zahnrädern im Getriebezug verwenden. Weniger Zahnräder / Zug = mehr Züge ** = größerer und besserer Zerstörungsroboter.

Die Eingabe besteht aus einer Zeile:

X,Y,B,A

X und Y sind die Koordinaten des letzten Gangs. Der erste Gang befindet sich immer bei (0,0). B und A sind die Radien der End- bzw. Anfangsgänge. Um einige Schwierigkeiten hinzuzufügen, müssen Sie sicherstellen, dass sich das Ausgangszahnrad in die richtige Richtung dreht. Wenn A und B dasselbe Vorzeichen haben, muss sich das Ausgangszahnrad in die gleiche Richtung drehen, und es muss eine ungerade Anzahl von Zahnrädern verwendet werden. Wenn sie entgegengesetzte Vorzeichen haben, muss eine gerade Anzahl von Zahnrädern verwendet werden.

Die Ausgabe sollte eine Liste der X-Position, Y-Position und Radien jedes zusätzlichen Zahnrads sein, ein Zahnrad pro Zeile. Wenn es mehrere Lösungen für minimale Zahnräder gibt, drucken Sie nur eine Ihrer Wahl. Die Reihenfolge der Gänge in der Ausgabe spielt keine Rolle.

Beispiele (äquivalentere Lösungen sind möglich):

in
4,0,1,1
out
2,0,1

in
7,7,-2,-2
out
4,3,3
OR
0,7,5
OR
the above reflected over y=x line

in
7,8,-1,2
out
7,0,5
7,6,1
OR
7,0,5
1,8,5

in
7,7,2,-2
out
4,-3,3
7,1,2
12,1,3
12,7,3
OR
any permutation of the above, or reflected over y=x line
Now you're thinking with gear trains!

Hier sind die Lösungen für die obigen Beispiele dargestellt:

Bildbeschreibung hier eingeben

Soweit ich weiß, ist kein Problem unmöglich, wenn sich die beiden Eingangsräder nicht überlappen oder direkt verbinden. Damit müssen Sie sich nicht befassen.

Dies ist Code Golf, die kürzeste Antwort gewinnt.


* Eine zukünftige KOTH, irgendjemand?

**CHOO CHOO!!

PhiNotPi
quelle
Ich hätte es so, dass sowohl der Anfangsradius als auch der Endradius negativ sein können.
wizzwizz4
9
Willkommen zu Phis Lego Gear Train Challenge. Nach 4 Jahren in der Sandbox war es hoffentlich das Gewicht wert.
ein Spaghetto
@ wizzwizz4 Die Änderung vorgenommen.
PhiNotPi
War das wirklich 4 Jahre im Sandkasten?
6.
@RikerW Mehr wie 3 1/3.
PhiNotPi

Antworten:

1

C #, 660 Bytes

using System.Linq;using System;class P{int p=1,x,y,r;P l;static void Main(){var Q="$&.$'7$(@$*R$'/$(8$)A'(A('A$+S$(0$)9'(9('9$*B$,T$*2$+;$,D$.V*,V,*V";var I=Console.ReadLine().Split(',').Select(int.Parse).ToList();int i=0,t,s=7,u,v,w,p=I[3]*I[2];for(var D=new[]{new P{r=Math.Abs(I[3]),l=new P{r=Math.Abs(I[2]),x=I[0],y=I[1],p=3}}}.ToList();i>=0;){P c=D[i++],l=c.l;for(;(l=l?.l)!=null&&(s=(t=c.x-l.x)*t+(t=c.y-l.y)*t-(t=c.r+l.r)*t)>0;);if(s==0&&l.p>2&p*c.p<0)for(i=-1;c.l.p<3;c=c.l)Console.WriteLine(c.x+","+c.y+","+c.r);for(t=0;s>0&t<66;t++)for(u=Q[t++]-36,v=Q[t++]-36,s=1;s++<5&Q[t]%9==c.r;w=u,u=v,v=-w,D.Add(new P{l=c,r=Q[t]/9-4,x=c.x+u,y=c.y+v,p=-c.p}));}}}

Probieren Sie es online

Das hat sehr viel Spaß gemacht !! Vollständiges Programm, akzeptiert Eingaben von STDIN und gibt sie an STDOUT aus. Ausgang ist die Zahnräder in der Reihenfolge vom Ende bis zum Start. Verwendung:

Führt eine einfache Breitensuche durch, die ein 4-Gang-Problem in weniger als einer Sekunde löst. Der Verzweigungsfaktor ist eigentlich nicht so groß, also sollte er für wesentlich mehr gut sein (nicht wirklich getestet). Leider benutzt es Linq.

Die QZeichenfolge ist eine Tabelle aller zulässigen Zahnradverbindungen (dh ein r=3und eine Verbindung zu einem r=1if dx=4und dy=0) in einem Quadranten, die dann gedreht werden, um die anderen zu finden. Jeder Satz von 3 Bytes ist dx, dyund der Radius - Informationen für eine rechtliche Verbindung. Die Wahl (als Offset war sehr bewusst: Es hat Spaß gemacht, einmal ein ASCII-Zeichen für nette Eigenschaften auszuwählen, anstatt verzweifelt nach netten Eigenschaften für aufgezwungene ASCII-Zeichen zu suchen.

Ich kann die Eingabe wahrscheinlich besser lesen, aber ich hatte noch kein Glück, nicht zuletzt, weil der Linq durch die Notwendigkeit einer Liste bezahlt wird. Ich bin auch sehr enttäuscht über den Rotationscode. Ich habe das Gefühl, dass dies in erheblich weniger Bytes erledigt werden könnte.

Formatierter und kommentierter Code mit QGenerator:

using System.Linq; // seems to pay today
using System;

class P
{
    static void GenQ()
    {
        int t, k = 0, m = 0;
        Func<P, P, int> C = (P c, P l) => (t = c.x - l.x) * t + (t = c.y - l.y) * t - (t = c.r + l.r) * t; // ==0 -> touching, <0 -> not touching, >0 -> overlap

        string str = "";

        string T(int i) => "" + (char)('$' + i); // $ is zero, '$' == 36, so we can mod and div by 9, and greater than " so we don't have to escape it

        foreach (int r in new[] { 1, 2, 3, 5 }) // at 0,0 (current gear)
            foreach (int s in new[] { 1, 2, 3, 5 }) // gear to place
                for (int i = 0; i <= r + s; i++) // x
                    for (int j = 1; j <= r + s; j++, m++) // y
                        if (C(new P { r = r }, new P { r = s, x = i, y = j }) == 0) // 
                        {
                            str += T(i) + T(j) + T(r + s * 9);
                            k++;
                        }

        System.Console.WriteLine("K : " + k);
        System.Console.WriteLine("M : " + m);
        System.Console.WriteLine(str);
        System.Console.ReadKey(true);
        return;
    }

    int p=1, // parity
        x, // x
        y, // y
        r; // radias (TODO: store radias^2 ?)
    P l; // previous in search list

    static void Main()
    {
        //GenQ();

        // '$' == 36 (4*9)
        // 3char blocks: x,y,r+9*s
        var Q="$&.$'7$(@$*R$'/$(8$)A'(A('A$+S$(0$)9'(9('9$*B$,T$*2$+;$,D$.V*,V,*V"; // quarter table

        // primative read ints
        var I=Console.ReadLine().Split(',').Select(int.Parse).ToList();

        int i=0, // position in Due
            t, // check differential cache, position in Q
            s=7, // check cache, rotation counter (>0)
            u, // rotation x
            v, // rotation y
            w, // rotation x cache
            p=I[3]*I[2]; // parity (>0 -> same, even ; <0 -> different, odd)

        // due (not point using a queue, the search space grows exponentially)
        for(var D=new[]{
                new P{r=Math.Abs(I[3]), // start (p==1)
                    l=new P{r=Math.Abs(I[2]),x=I[0],y=I[1],p=3} // terminal (detect with p==3)
                }}.ToList();
            i>=0;) // infinite number of configurations, no bounds, i is escape term
        {
            P c=D[i++], // current
                l=c.l; // check, initially the one before the previous (we know we are touching last already)

            // 'checks' c against l
            //Func<int>C=()=>(t=c.x-l.x)*t+(t=c.y-l.y)*t-(t=c.r+l.r)*t; // ==0 -> touching, >0 -> not touching, <0 -> overlap

            // check we arn't touching any before us (last thing we check is terminal)
            for(;(l=l?.l)!=null&& // for each before us (skipping the first one)
                (s=(t=c.x-l.x)*t+(t=c.y-l.y)*t-(t=c.r+l.r)*t)>0;); // check c against l and cache in s, ==0 -> touching, >0 -> not touching, <0 -> overlap

            if(s==0&& // touching last checked?
                l.p>2& // stopped on terminal?
                p*c.p<0) // correct parity? -> win
                for(i=-1; // escape
                    c.l.p<3;c=c.l) // for each that wasn't the first
                    Console.WriteLine(c.x+","+c.y+","+c.r);

            // enumerate possible additions, and queue them in due
            for(t=0;
                s>0& // not touching or overlapping anything (including terminal)
                t<66;t++) // 66 = Q.Length
                for(
                    u=Q[t++]-36, // '$'
                    v=Q[t++]-36,
                    s=1;s++<5& // rotate 4 times
                    Q[t]%9==c.r; // our raidus matches
                        w=u, // chache y value
                        u=v, // rotate
                        v=-w,
                        D.Add(new P // add
                        {
                            l=c,
                            r=Q[t]/9-4, // radius
                            x=c.x+u,
                            y=c.y+v,
                            p=-c.p // flip parity
                        }));
        }
    }
}
VisualMelon
quelle