Metagolf Berbintang

25

Starry adalah bahasa pemrograman esoteris lucu di mana kode hanya terdiri dari di +*.,`'mana perintah aktual yang diwakili oleh masing-masing karakter ditentukan oleh jumlah ruang di depannya. Itu membuatnya sulit bahkan untuk tantangan fixed-output golf, karena perintah yang berbeda dapat menjelaskan jumlah byte yang sangat berbeda. Secara khusus, literal angka memiliki representasi unary yang membuatnya perlu untuk membangun jumlah yang lebih besar dengan beroperasi pada yang lebih kecil.

Oleh karena itu, tantangan ini adalah tentang menulis program yang dapat golf program Starry tersebut.

Bagaimana cara kerja Starry?

(Beberapa detail dibiarkan tidak ditentukan pada esolang, jadi saya akan membahas perilaku penerjemah Ruby .)

Starry adalah bahasa berbasis tumpukan, dengan satu tumpukan nilai integer presisi arbitrer (yang awalnya kosong).

Satu-satunya karakter yang bermakna adalah:

+*.,`'

dan spasi. Semua karakter lain diabaikan. Setiap urutan spasi diikuti oleh salah satu karakter non-spasi tersebut mewakili instruksi tunggal. Jenis instruksi tergantung pada karakter non-spasi dan jumlah spasi.

Petunjuknya adalah:

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. 

Perhatikan bahwa interpreter memindai kode sumber untuk label sebelum eksekusi dimulai, sehingga dimungkinkan untuk melompat ke depan dan juga ke belakang.

Tentu saja, Starry juga memiliki perintah input (menggunakan ,analog dengan .), tetapi itu tidak relevan untuk tantangan ini.

Tantangan

Diberi string, hasilkan program Starry yang tidak mengambil input dan mencetak string itu persis ke STDOUT.

Anda dapat menulis sebuah program atau fungsi, mengambil input melalui STDIN (atau alternatif terdekat), argumen baris perintah atau argumen fungsi dan mengeluarkan hasilnya melalui STDOUT (atau alternatif terdekat), nilai pengembalian fungsi atau parameter function (out).

Anda dapat mengasumsikan bahwa string tidak lebih dari 128 karakter dan hanya terdiri dari karakter ASCII yang dapat dicetak (titik kode 0x20 hingga 0x7E).

Solusi Anda harus memproses input semacam itu dalam waktu kurang dari 5 menit pada mesin desktop yang masuk akal (ada beberapa kelonggaran untuk ini; jika dibutuhkan beberapa menit lagi di laptop saya, saya tidak keberatan, tetapi jika butuh 15, saya akan mendiskualifikasi saya t).

Solusi Anda akan diuji pada sejumlah string berbeda yang tercantum di bawah ini. Skor Anda adalah jumlah total byte dari program Starry terkait. Dalam kasus seri, metagolfer terpendek menang. Artinya, jangan repot-repot bermain golf kode Anda sendiri kecuali ada dasi (yang saya pikir hanya akan terjadi jika solusi optimal mungkin).

Anda tidak boleh mengoptimalkan kode Anda terhadap kasus uji khusus yang tercantum di bawah ini. Khususnya, Anda seharusnya tidak membuat solusi kerajinan tangan untuk mereka. Mengoptimalkan kelas string yang strukturnya mirip dengan string yang diberikan baik-baik saja. Jika saya mencurigai siapa pun dari solusi hardcoding, saya berhak untuk mengganti beberapa atau semua kasus uji (dengan string struktur yang sebanding).

Uji Kasus

Setiap baris adalah test case terpisah:

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

Kredit untuk test case kedua diberikan kepada Dennis . Kredit untuk test case keempat pergi ke Sp3000.

Solusi Referensi

Berikut ini adalah solusi referensi yang sangat mendasar di CJam:

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

Anda dapat menjalankannya terhadap seluruh test suite di sini. Skornya adalah:

1233
5240
4223
11110
7735
10497
11524
11392
Total: 62954

Ini adalah pendekatan paling sederhana yang mungkin: dorong setiap titik kode karakter sebagai literal, lalu cetaklah. Itu tidak menggunakan perbedaan kecil antara karakter berturut-turut, pencetakan integer, bagian berulang string dll. Saya akan menyerahkan hal-hal itu kepada Anda.

Saya percaya ada banyak ruang untuk perbaikan. Sebagai referensi, kerajinan tangan terpendek "Halo, Dunia!" hanya sepanjang 169 byte.

Martin Ender
sumber

Jawaban:

6

Ruby, 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}

Metode ini starrymenjawab pertanyaan yang diberikan.

Hasil:

230
639
682
1974
1024
1897
2115
2436
Total: 10997

Bagaimana itu bekerja

shortestadalah algoritma utama. Dibutuhkan satu nomor dan menemukan cara terpendek untuk meletakkannya di tumpukan, atau mengambil dua angka, dan mengembalikan kode untuk meletakkan yang kedua di tumpukan dengan asumsi yang pertama sudah aktif. $sadalah Hash untuk menahan hasil operasi ini untuk penggunaan lebih lanjut.

starrymengambil string dan membaginya menjadi array kode karakter (atau angka untuk dicerna). Dimulai kode dengan satu nomor di bagian bawah tumpukan. Selanjutnya menghitung cara terpendek yang dapat menghasilkan setiap nomor berturut-turut, mungkin menyalin yang terakhir atau menggunakan nomor yang diletakkan di tumpukan di awal.

MegaTom
sumber
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)

Fungsi yang relevan adalah yang tepat dinamai metagolf.

Hasilnya adalah:

210
676
684
2007
1463
2071
2204
2530
Total: 11845

Anda dapat menemukan hasil lengkapnya di sini .

Penjelasan singkat

Saya akan membuat penjelasan singkat karena ada banyak hal yang masih harus diperbaiki.

Algoritma dasar hanya melihat pasangan karakter, dan menemukan cara optimal untuk beralih dari satu karakter ke karakter lain melalui BFS. Digit saat ini didorong dan dicetak segera, meskipun ini akan diubah kemudian.

Ada juga sedikit terpanjang-umum yang terjadi, untuk meninggalkan beberapa elemen di stack untuk digunakan kembali nanti. Ini tidak sebagus pengulangan, tetapi memberikan penghematan yang layak.

Sp3000
sumber
Hore, seseorang untuk berperang :-) Tentu saja, sekarang saya melihat bahwa perjalanan saya masih panjang ...
ETHproduksi
5

JavaScript, 25158 23778

Sekarang kompatibel dengan 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;
}

Hasil:

432
949
2465
3996
1805
3551
5205
5375
Total: 23778

Awal yang bagus menurut saya, tapi jelas belum selesai. Alih-alih membuat masing-masing karakter secara terpisah, ini menambah atau mengurangi dari kode karakter sebelumnya. Saya akan menambahkan penjelasan lengkap ketika saya selesai bermain golf.

Produksi ETH
sumber
Yap, ia bekerja di Firefox sekarang, meskipun Chrome masih mengeluh charCodeAt.
Martin Ender