Metagolf stellato


25

Starry è un divertente linguaggio di programmazione esoterica in cui il codice è costituito solo da +*.,`'dove il comando effettivo rappresentato da ciascuno di quei caratteri è determinato dal numero di spazi davanti ad esso. Ciò rende complicato persino il golf delle sfide di output fisso, poiché comandi diversi possono rappresentare un numero di byte molto diverso. In particolare, i letterali numerici hanno una rappresentazione unaria che rende necessario costruire numeri più grandi operando su quelli più piccoli.

Pertanto, questa sfida riguarda la scrittura di un programma che può giocare a golf con tali programmi stellati.

Come funziona Starry?

(Alcuni dettagli sono lasciati non specificati sugli esolang, quindi vado con il comportamento dell'interprete Ruby .)

Starry è un linguaggio basato su stack, con una singola pila di valori interi di precisione arbitraria (che inizialmente è vuota).

Gli unici personaggi significativi sono:

+*.,`'

e spazi. Tutti gli altri personaggi vengono ignorati. Ogni sequenza di spazi seguita da uno di quei caratteri non spaziali rappresenta una singola istruzione. Il tipo di istruzione dipende dal carattere non spaziale e dal numero di spazi.

Le istruzioni sono:

Spaces  Symbol  Meaning
0         +     Invalid opcode.
1         +     Duplicate top of stack.
2         +     Swap top 2 stack elements.
3         +     Rotate top 3 stack elements. That is, send the top stack element
                two positions down. [... 1 2 3] becomes [... 3 1 2].
4         +     Pop and discard top of stack.
n ≥ 5     +     Push n − 5 to stack.
0 mod 5   *     Pop y, pop x, push x + y.
1 mod 5   *     Pop y, pop x, push x − y.
2 mod 5   *     Pop y, pop x, push x * y.
3 mod 5   *     Pop y, pop x, push x / y, rounded towards -∞.
4 mod 5   *     Pop y, pop x, push x % y. The sign of the result matches the sign of y.
0 mod 2   .     Pop a value and print it as a decimal number.
1 mod 2   .     Pop a value and print it as an ASCII character. This throws an error
                if the value is not in the range [0, 255].
n         `     Mark label n.
n         '     Pop a value; if non-zero, jump to label n. 

Si noti che l'interprete esegue la scansione del codice sorgente per le etichette prima che inizi l'esecuzione, quindi è possibile saltare sia in avanti che all'indietro.

Naturalmente, Starry ha anche comandi di input (da usare in modo ,analogo a .), ma questi sono irrilevanti per questa sfida.

La sfida

Data una stringa, genera un programma stellato che non accetta input e stampa quella stringa esattamente su STDOUT.

È possibile scrivere un programma o una funzione, prendendo l'input tramite STDIN (o l'alternativa più vicina), l'argomento della riga di comando o l'argomento della funzione e producendo il risultato tramite STDOUT (o l'alternativa più vicina), il valore di ritorno della funzione o il parametro della funzione (out).

Si può presumere che la stringa non sia più lunga di 128 caratteri e che sarà composta solo da caratteri ASCII stampabili (punti di codice da 0x20 a 0x7E).

La tua soluzione dovrebbe elaborare tali input in meno di 5 minuti su una macchina desktop ragionevole (c'è un margine di manovra per questo; se ci vorrà qualche minuto in più sul mio laptop non mi dispiace, ma se ci vogliono 15, squalificherò esso).

La tua soluzione verrà testata su una serie di stringhe diverse elencate di seguito. Il tuo punteggio è il conteggio totale dei byte dei corrispondenti programmi stellati. In caso di pareggio, vince il metagolfer più corto. Cioè, non preoccuparti di giocare a golf con il tuo codice a meno che non ci sia un pareggio (che penso accadrà solo nel caso in cui sia possibile una soluzione ottimale).

Non è necessario ottimizzare il codice per i casi di test specifici elencati di seguito. In particolare, non dovresti hardcodificare soluzioni artigianali per loro. L'ottimizzazione verso classi di stringhe la cui struttura è simile a quella delle stringhe date va bene. Se sospetto che qualcuno abbia soluzioni hardcoding, mi riservo il diritto di sostituire alcuni o tutti i casi di test (con stringhe di strutture comparabili).

Casi test

Ogni riga è un caso di test separato:

Hello, World!
pneumonoultramicroscopicsilicovolcanoconiosis
.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.
Hickory, dickory, dock. The mouse ran up the clock. The clock struck 1. The mouse ran down. Hickory, dickory, dock.
36912059868043514648560046917066768694455682545071266675083273015450033938555319356951628735735013250100789433961153496780296165
bVZ48121347GLtpYnt76CZSxTpMDs6791EJE808077eySXldY162424ddTB90707UupwlWGb63618542VhA252989453TXrWgqGm85899uHOAY2oAKE198GOVUttvW63
7MYxoWBNt180CDHS5xBGvU70HHVB17bh8jYzIIiU6n6g98Rose1nOe8Svcg56nax20q30kT3Ttb2jHl5q2Iuf1vPbjPxm9cyKXwxc0OUK8pr13b2n7U9Y7RwQTc26A1I
n9}unwxVa}[rj+5em6K#-H@= p^X/:DS]b*Jv/_x4.a5vT/So2R`yKy=in7-15B=g _BD`Bw=Z`Br;UwwF[{q]cS|&i;Gn4)q=`!G]8"eFP`Mn:zt-#mfCV2AL2^fL"A

I crediti per il secondo test case vanno a Dennis . I crediti per il quarto test case vanno a Sp3000.

Soluzione di riferimento

Ecco una soluzione di riferimento davvero semplice in CJam:

q{S5*\iS*'+S'.}%

Puoi eseguirlo su tutta la suite di test qui. I punteggi sono:

1233
5240
4223
11110
7735
10497
11524
11392
Total: 62954

È l'approccio più semplice possibile: spingere il punto di codice di ciascun personaggio come un valore letterale, quindi stamparlo. Non fa uso di piccole differenze tra caratteri consecutivi, stampa di numeri interi, parti ripetitive della stringa, ecc. Lascerò queste cose a te.

Credo che ci sia molto margine di miglioramento. Per riferimento, il più breve realizzato a mano "Hello, World!" è lungo solo 169 byte.

Risposte:


6

Rubino, 13461 10997

$s = {};
def shortest a,b=nil
    return $s[[a,b]] if $s[[a,b]]
    l = []
    if b
        if a == b
            return $s[[a,b]] = ""
        elsif a > b
            l.push shortest(a-b)+" *"
            l.push " +   *"+shortest(1,b) if a > 1
            l.push " + *"+shortest(0,b) if a > 0
            l.push "    +"+shortest(b)
        elsif a < b
            l.push " +  *"+shortest(a*a,b) if a*a>a && a*a<=b
            l.push " +*"+shortest(a+a,b) if a+a<=b && a+a>a
            l.push shortest(b-a)+"*"
            l.push " +"+shortest(a,b/a)+"  *" if a>2 && b%a == 0
            l.push " +"+shortest(a,b-a)+"*" if a>1 && b>a*2
        end
    else
        l.push ' '*(a+5)+'+' #if a < 6
        (1..a/2).each {|n|
            l.push shortest(n)+shortest(n,a)
        }
    end
    return $s[[a,b]] = l.min_by{|x|x.length}
end

def starry(str)
    arr = str.bytes.map{|b|
        if b>47 && b<58
            b-48# change digets to numbers
        else
            b
        end
    }

    startNum = (1..128).min_by{|x|arr.inject{|s,y|s + [shortest(x,y).length+2,shortest(y).length].min}+shortest(x).length}
    #one number to be put on the stack at the start.

    code = shortest(startNum)
    code += [
        shortest(arr[0]),
        " +"+shortest(startNum, arr[0])
    ].min_by{|x|x.length}

    arr.each_cons(2) do |a|
        pr = a[0]<10?'.':' .'
        code += [
            pr+shortest(a[1]),
            " +"+pr+shortest(a[0], a[1]),
            pr+" +"+shortest(startNum, a[1])
        ].min_by{|x|x.length}
    end
    code += arr[-1]<10?'.':' .'
end

a = ["Hello, World!",
"pneumonoultramicroscopicsilicovolcanoconiosis",
".oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.",
"Hickory, dickory, dock. The mouse ran up the clock. The clock struck 1. The mouse ran down. Hickory, dickory, dock.",
"36912059868043514648560046917066768694455682545071266675083273015450033938555319356951628735735013250100789433961153496780296165",
"bVZ48121347GLtpYnt76CZSxTpMDs6791EJE808077eySXldY162424ddTB90707UupwlWGb63618542VhA252989453TXrWgqGm85899uHOAY2oAKE198GOVUttvW63",
"7MYxoWBNt180CDHS5xBGvU70HHVB17bh8jYzIIiU6n6g98Rose1nOe8Svcg56nax20q30kT3Ttb2jHl5q2Iuf1vPbjPxm9cyKXwxc0OUK8pr13b2n7U9Y7RwQTc26A1I",
"n9}unwxVa}[rj+5em6K#-H@= p^X/:DS]b*Jv/_x4.a5vT/So2R`yKy=in7-15B=g _BD`Bw=Z`Br;UwwF[{q]cS|&i;Gn4)q=`!G]8\"eFP`Mn:zt-#mfCV2AL2^fL\"A"]
c = a.map{
    |s|
    starry(s).length
}
p c.inject(0){|a,b|a+b}

Il metodo starryrisponde alla domanda data.

risultati:

230
639
682
1974
1024
1897
2115
2436
Total: 10997

Come funziona

shortestè l'algoritmo principale. Prende un numero e trova il modo più breve per posizionarlo nello stack oppure impiega due numeri e restituisce il codice per inserire il secondo nello stack supponendo che il primo sia già attivo. $sè un hash per conservare i risultati di queste operazioni per un ulteriore utilizzo.

starryprende una stringa e la divide in una matrice di codici carattere (o numeri per i digest). Inizia il codice con un numero nella parte inferiore dello stack. Successivamente calcola il modo più breve in cui può generare ogni numero successivo, eventualmente copiando l'ultimo o usando il numero messo in pila all'inizio.


9

Python 3, 17071 11845

from functools import lru_cache
import heapq
import time

cases = r"""
Hello, World!
pneumonoultramicroscopicsilicovolcanoconiosis
.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.
Hickory, dickory, dock. The mouse ran up the clock. The clock struck 1. The mouse ran down. Hickory, dickory, dock.
36912059868043514648560046917066768694455682545071266675083273015450033938555319356951628735735013250100789433961153496780296165
bVZ48121347GLtpYnt76CZSxTpMDs6791EJE808077eySXldY162424ddTB90707UupwlWGb63618542VhA252989453TXrWgqGm85899uHOAY2oAKE198GOVUttvW63
7MYxoWBNt180CDHS5xBGvU70HHVB17bh8jYzIIiU6n6g98Rose1nOe8Svcg56nax20q30kT3Ttb2jHl5q2Iuf1vPbjPxm9cyKXwxc0OUK8pr13b2n7U9Y7RwQTc26A1I
n9}unwxVa}[rj+5em6K#-H@= p^X/:DS]b*Jv/_x4.a5vT/So2R`yKy=in7-15B=g _BD`Bw=Z`Br;UwwF[{q]cS|&i;Gn4)q=`!G]8"eFP`Mn:zt-#mfCV2AL2^fL"A
""".strip().splitlines()

@lru_cache(maxsize=128)
def shortest_m_to_n(m, n):
    if m is None:
        L = []
    else:
        L = [m]

    to_search = [[0, "", L]]
    seen = set()

    while True:
        length, code, stack = heapq.heappop(to_search)

        if len(stack) == 1 and stack[-1] == n:
            return code

        seen.add(tuple(stack))
        options = []

        for i in range(1, 11):
            new_stack = stack[:] + [i]
            new_code = code + ' '*(i+5) + '+'
            options.append([len(new_code), new_code, new_stack])

        if stack:
            new_stack = stack[:] + [stack[-1]]
            new_code = code + " +"
            options.append([len(new_code), new_code, new_stack])

        if len(stack) >= 2:
            x, y = stack[-2:]

            for i, op in enumerate(['+', '-', '*', '//', '%']):
                try:
                    new_elem = eval("{}{}{}".format(x, op, y))
                    new_stack = stack[:-2] + [new_elem]
                    new_code = code + ' '*i + '*'
                    options.append([len(new_code), new_code, new_stack])

                except ZeroDivisionError:
                    pass

        for op in options:
            if tuple(op[2]) in seen or len(op[2]) > 4 or op[2][-1] > 200:
                continue

            heapq.heappush(to_search, op)

def lcs(s1, s2):
    dp_row = [""]*(len(s2)+1)

    for i, c1 in enumerate(s1):
        new_dp_row = [""]

        for j, c2 in enumerate(s2):
            if c1 == c2 and not c1.isdigit():
                new_dp_row.append(dp_row[j] + c1)
            else:
                new_dp_row.append(max(dp_row[j+1], new_dp_row[-1], key=len))

        dp_row = new_dp_row

    return dp_row[-1]

def metagolf(s):
    keep = ""
    split_index = 0

    for i in range(1, len(s)):
        l = lcs(s[:i], s[i:][::-1])
        if len(l) > len(keep):
            keep = l
            split_index = i

    code = []
    stack = []
    keep_ptr = 0
    i = 0

    while i < len(s):
        c = s[i]
        n = ord(c)

        if c in "0123456789":
            code += [" "*(int(c)+5) + "+."]
            i += 1
            continue

        if stack:
            if stack[-1] == n:
                code += [" +", " ."]
            elif len(stack) >= 2 and stack[-2] == n:
                for j in range(len(code)):
                    if code[~j] == " +":
                        code[~j] = ""
                        break

                code += [" +", " ."]
                stack.pop()
            else:
                code += [shortest_m_to_n(stack[-1], n), " +", " ."]
                stack[-1] = n

        else:
            code += [shortest_m_to_n(None, n), " +", " ."]
            stack.append(n)

        while i < split_index and keep[keep_ptr:][:1] == c:
            code += [" +"]
            keep_ptr += 1
            stack.append(n)

        i += 1

    code = "".join(code)

    if code[-4:] == " + .":
        code = code[:-4] + " ."

    return code

total = 0

for case in cases:
    start_time = time.time()

    s = metagolf(case)
    print(len(s), time.time() - start_time)
    total += len(s)
    print(s)
    print('='*50)

print(total)

La funzione rilevante è il nome appropriato metagolf.

I risultati sono:

210
676
684
2007
1463
2071
2204
2530
Total: 11845

Puoi trovare l'output completo qui .

Breve spiegazione

Terrò breve la spiegazione poiché ci sono ancora molte cose da migliorare.

L'algoritmo di base esamina solo coppie di caratteri e trova il modo ottimale per passare da un carattere all'altro tramite BFS. Le cifre vengono attualmente inviate e stampate immediatamente, anche se verranno modificate in seguito.

C'è anche un po 'di sottosequenza comune più lunga in corso, per lasciare alcuni elementi in pila per il riutilizzo in seguito. Non è buono come la ripetizione, ma offre risparmi decenti.


Evviva, qualcuno da combattere :-) Certo, ora vedo che il mio ha una lunga strada da percorrere ...
ETHproductions

5

JavaScript, 25158 23778

Ora compatibile ES5!

String.prototype.repeat = String.prototype.repeat || function (n) { return Array(n+1).join(this); }

function starrify(x) {
  function c(x){return x.charCodeAt()}
  var char = x[0], result = ' '.repeat(c(char)+5)+'+ + .';
  x=x.slice(1);
  for(var i in x) {
    if (char < x[i]) {
      result += ' '.repeat(c(x[i])-c(char)+5)+'+* + .';
    } else if (char > x[i]) {
      if(c(char)-c(x[i]) < c(x[i])) {
        result += ' '.repeat(c(char)-c(x[i])+5)+'+ * + .';
      } else {
        result += ' '.repeat(c(x[i])+5)+'+ + .';
      }
    } else {
      result += ' + .';
    }
    char = x[i];
  }
  return result;
}

risultati:

432
949
2465
3996
1805
3551
5205
5375
Total: 23778

Un buon inizio secondo me, ma ovviamente non finito. Invece di creare ogni carattere separatamente, aggiunge o sottrae dal precedente codice carattere. Aggiungerò una spiegazione completa quando avrò finito il meta-golf.


Sì, ora funziona in Firefox, anche se Chrome si lamenta ancora charCodeAt.
Martin Ender,
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.