Lego Gear Train


13

Ispirato alla sfida dei rapporti di cambio Lego di Keith Randall.

Anch'io ho intenzione di costruire un gigantesco robot lego che alla fine sarà in grado di distruggere gli altri robot nella competizione mai menzionata prima. * Nel processo di costruzione del robot, userò molti ingranaggi per connettermi diverse parti del robot. Voglio che tu mi scriva il programma più breve che mi aiuterà a costruire i complessi ingranaggi necessari per un compito così complesso. Ovviamente userò solo ingranaggi con raggio 1, 2, 3 e 5 unità di lego arbitrarie.

Ogni ingranaggio nel treno di ingranaggi ha una coordinata intera specifica su una griglia 2D. La prima marcia si trova in (0,0) e la marcia finale si troverà in coordinate non negative. La posizione e le dimensioni del primo e dell'ultimo ingranaggio verranno fornite come input, il programma deve dire quali ingranaggi vanno dove riempire gli spazi vuoti.

Inoltre, il programma deve utilizzare il numero minimo possibile di ingranaggi nella trasmissione. Meno ingranaggi / treno = più treni ** = robot di distruzione più grande e migliore.

L'input consisterà in una riga:

X,Y,B,A

X e Y sono le coordinate della marcia finale. La prima marcia si trova sempre in (0,0). B e A sono i raggi degli ingranaggi finale e iniziale, rispettivamente. Per aggiungere qualche difficoltà, è necessario assicurarsi che l'ingranaggio di uscita ruoti nella direzione corretta. Se A e B hanno lo stesso segno, l'ingranaggio di uscita deve ruotare nella stessa direzione e deve essere utilizzato un numero dispari di marce. Se hanno segni opposti, è necessario utilizzare un numero pari di marce.

L'output dovrebbe essere un elenco della posizione X, della posizione Y e dei raggi di ciascuna marcia aggiuntiva, una marcia per linea. Se sono disponibili più soluzioni con ingranaggi minimi, stampare solo una a scelta. L'ordine degli ingranaggi nell'output non ha importanza.

Esempi (potrebbero essere possibili soluzioni più equivalenti):

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!

Ecco le soluzioni agli esempi precedenti, visualizzate:

inserisci qui la descrizione dell'immagine

Per quanto ne so, nessun problema è impossibile a meno che i due ingranaggi di input non si sovrappongano o si colleghino direttamente. Non dovrai occupartene.

Questo è il golf del codice, vince la risposta più breve.


* Un futuro KOTH, chiunque?

** CHOO CHOO !!


Lo farei in modo che sia il raggio iniziale che quello finale possano essere negativi.
wizzwizz4,

9
Benvenuti alla sfida Lego Gear Train di Phi. Dopo 4 anni nella Sandbox, speriamo che ne sia valsa la pena.
uno spaghetto

@ wizzwizz4 Ha apportato la modifica.
PhiNotPi

È stato davvero nella sandbox per 4 anni?
Rɪᴋᴇʀ

@RikerW Più come 3 1/3.
PhiNotPi

Risposte:


1

C #, 660 byte

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

Provalo online

È stato molto divertente !! Programma completo, accetta input da STDIN, output a STDOUT. L'output è gli ingranaggi in ordine dalla fine all'inizio. Uso:

Esegue una semplice prima ricerca dell'ampiezza, che risolve un problema a 4 marce in meno di un secondo. Il fattore di ramificazione non è in realtà così grande, quindi dovrebbe essere buono per molto di più (non lo ha davvero testato). Purtroppo usa Linq.

La Qstringa è una tabella di tutte le connessioni a ingranaggi consentite (ovvero un r=3e connettersi a un r=1if dx=4edy=0 ) in un quadrante, che viene quindi ruotato per trovare gli altri. Ogni set di 3 byte è il dx, dye le informazioni raggio per un nesso giuridico. La scelta di (un offset è stata molto deliberata: è stato divertente per una volta scegliere un carattere ASCII per belle proprietà, piuttosto che cercare disperatamente di trovare belle proprietà per i caratteri ASCII imposti.

Probabilmente posso fare un lavoro migliore nel leggere l'input, ma non ho ancora avuto fortuna, anche perché Linq è pagato dalla necessità di un elenco. Sono anche molto deluso dal codice di rotazione, penso che potrebbe essere fatto in considerevolmente meno byte.

Codice formattato e commentato con Qgeneratore:

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
                        }));
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.