Зоряний метагольф


25

Starry - це смішна езотерична мова програмування, в якій код складається лише +*.,`'там, де фактична команда, представлена ​​кожним із цих символів, визначається кількістю пробілів перед нею. Це робить складним навіть проблеми з фіксованим виведенням у гольф, тому що різні команди можуть складати дуже різну кількість байтів. Зокрема, літерали чисел мають одинарне представлення, що обумовлює необхідність нарощування більшої кількості, працюючи на менших.

Тому це завдання полягає в написанні програми, яка може гольфувати такі програми Starry.

Як працює Starry?

(На езоланге залишається не вказано кілька деталей, тому я йду з поведінкою перекладача Рубі .)

Starry - мова на основі стека, що має єдиний стек цілих значень довільної точності (який спочатку порожній).

Єдині значущі символи:

+*.,`'

і пробіли. Усі інші символи ігноруються. Кожна послідовність пробілів, за якими слідує один з цих пробілів, являє собою одну інструкцію. Тип інструкції залежить від непробільного символу та кількості пробілів.

Інструкції:

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. 

Зауважте, що інтерпретатор сканує вихідний код для міток перед початком виконання, тому можливо стрибати вперед і назад.

Звичайно, у Starry також є команди введення (використовуючи ,аналогічно .), але вони не мають значення для цього завдання.

Змагання

Подавши рядок, створіть програму Starry, яка не вводить, і виведе ці строки точно в STDOUT.

Ви можете написати програму або функцію, взявши введення через STDIN (або найближчу альтернативу), аргумент командного рядка або аргумент функції та вивівши результат через STDOUT (або найближчу альтернативу), значення повернення функції або параметр функції (out).

Ви можете припустити, що рядок не більше 128 символів і що він буде складатися лише з друкованих символів ASCII (кодові точки від 0x20 до 0x7E).

Ваше рішення має обробити будь-який такий вхід менш ніж за 5 хвилин на розумному настільному верстаті (для цього є деякий час; якщо на моєму ноутбуці потрібно ще кілька хвилин, я не заперечую, але якщо це займе 15, я його дискваліфікую це).

Ваше рішення буде протестовано на ряді різних рядків, перелічених нижче. Ваш бал - це загальна кількість байтів відповідних програм Starry. У разі зв'язання виграє найкоротший метагольфер. Тобто, не турбуйте свій гольф, якщо не буде зрівняння (я думаю, це станеться лише в тому випадку, коли можливе оптимальне рішення).

Ви не повинні оптимізувати свій код до конкретних тестових випадків, перелічених нижче. Зокрема, не слід жорстко розробляти рішення, виготовлені вручну. Оптимізація до класів рядків, структура яких схожа на структуру даних рядків, є чудовою. Якщо я підозрюю кого-небудь у рішеннях жорсткого кодування, я залишаю за собою право замінити деякі або всі тестові випадки (рядками порівнянних структур).

Випробування

Кожен рядок є окремим тестом:

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

Кредити на другий тестовий випадок дістаються Деннісу . Кредити для четвертої тестової справи належать до Sp3000.

Довідкове рішення

Ось дійсно базове довідкове рішення в CJam:

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

Ви можете запустити його на весь тестовий набір тут. Оцінки:

1233
5240
4223
11110
7735
10497
11524
11392
Total: 62954

Це найпростіший можливий підхід: натисніть на кодову точку кожного символу як буквальну, а потім надрукуйте її. Тут не використовуються невеликі відмінності між послідовними символами, цілим друком, повторюваними частинами рядка і т. Д. Я залишу ці речі вам.

Я вважаю, що є багато можливостей для вдосконалення. Для довідки, найкоротший рукописний "Привіт, світ!" є лише 169 байт.

Відповіді:


6

Рубі, 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}

Метод starryвідповідає на задане питання.

Результати:

230
639
682
1974
1024
1897
2115
2436
Total: 10997

Як це працює

shortestє основним алгоритмом. Він займає одне число і знаходить найкоротший спосіб розмістити його в стеку, або він займає два числа, і повертає код, щоб помістити друге в стек, припускаючи, що перший вже ввімкнено. $sє хеш для зберігання результатів цих операцій для подальшого використання.

starryбере рядок і розбиває його на масив символьних кодів (або цифр для дайджестів). Він починає код з одного номера в нижній частині стека. Далі він обчислює найкоротший спосіб, коли він може генерувати кожне наступне число, можливо, копіюючи останнє або використовуючи число, поставлене на стеку на початку.


9

Пітон 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)

Відповідна функція - влучно названа metagolf.

Результати:

210
676
684
2007
1463
2071
2204
2530
Total: 11845

Ви можете знайти повний результат тут .

Коротке пояснення

Я буду тримати пояснення коротким, оскільки є багато речей, які ще мають бути вдосконалені.

Основний алгоритм просто розглядає пари символів і знаходить оптимальний шлях для переходу від одного знака до іншого через BFS. Наразі цифри висуваються та друкуються негайно, хоча це буде змінено пізніше.

Існує також невеликий найдовший-загальний підпорядкування, який залишається, щоб залишити кілька елементів у стеку для подальшого повторного використання. Це не так добре, як повторення, але забезпечує гідну економію.


Ура, хтось
бився

5

JavaScript, 25158 23778

Тепер сумісний із 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;
}

Результати:

432
949
2465
3996
1805
3551
5205
5375
Total: 23778

Гарний початок, на мою думку, але очевидно не закінчений. Замість того, щоб створювати кожну таблицю окремо, вона додає або віднімає з попереднього char-коду. Я додам повне пояснення, коли закінчу мета-гольф.


Так, зараз він працює у Firefox, хоча Chrome все ще скаржиться charCodeAt.
Мартін Ендер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.