별이 빛나는 메타 골프


25

Starry 는 재미있는 문자 프로그래밍 언어로, +*.,`'각 문자로 표시되는 실제 명령이 앞의 공백 수에 의해 결정되는 위치 로만 구성됩니다 . 다른 명령은 매우 다른 바이트 수를 설명 할 수 있기 때문에 고정 출력 문제를 해결하기가 까다로워집니다. 특히, 숫자 리터럴에는 단항 표현이있어 더 작은 숫자를 조작하여 더 큰 숫자를 만들어야합니다.

따라서이 과제는 그러한 Starry 프로그램을 골프로 칠 수있는 프로그램을 작성하는 것입니다.

별이 어떻게 작동합니까?

(몇 가지 세부 사항은 esolangs에 지정되어 있지 않으므로 Ruby 인터프리터 의 동작으로갑니다 .)

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에는 입력 명령 (와 ,유사하게 사용 .)이 있지만이 과제와 관련이 없습니다.

도전

문자열이 주어지면 입력을받지 않고 그 문자열을 STDOUT에 정확하게 인쇄하는 Starry 프로그램을 생성하십시오.

STDIN (또는 가장 가까운 대안), 명령 행 인수 또는 함수 인수를 통해 입력을 받고 STDOUT (또는 가장 가까운 대안), 함수 리턴 값 또는 함수 (out) 매개 변수를 통해 결과를 출력하는 프로그램 또는 함수를 작성할 수 있습니다.

문자열이 128자를 넘지 않고 인쇄 가능한 ASCII 문자 (코드 포인트 0x20 ~ 0x7E)로만 구성되어 있다고 가정 할 수 있습니다.

귀하의 솔루션은 합리적인 데스크탑 컴퓨터에서 5 분 이내에 이러한 입력을 처리해야합니다 (여기에 약간의 여지가 있습니다. 노트북에서 몇 분 더 걸리면 걱정하지 않지만 15 일이 걸리면 실격됩니다. 그것).

솔루션은 아래에 나열된 여러 가지 다른 문자열에서 테스트됩니다. 당신의 점수는 해당 Starry 프로그램의 총 바이트 수입니다. 동점 인 경우 가장 짧은 metagolfer가 이깁니다. 즉, 넥타이가 없다면 자신의 코드를 골퍼 링하지 마십시오 (최상의 솔루션이 가능한 경우에만 발생한다고 생각합니다).

아래 나열된 특정 테스트 사례에 맞게 코드를 최적화해서는 안됩니다. 특히 손으로 만든 솔루션을 하드 코딩해서는 안됩니다. 주어진 문자열과 구조가 유사한 문자열 클래스를 최적화하는 것이 좋습니다. 하드 코딩 솔루션을 사용하는 사람이 의심되는 경우 테스트 사례의 일부 또는 전부를 비슷한 구조의 문자열로 교체 할 권리가 있습니다.

테스트 사례

각 줄은 별도의 테스트 사례입니다.

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

두 번째 테스트 사례에 대한 크레딧은 Dennis 에게갑니다 . 네 번째 테스트 사례의 크레딧은 Sp3000으로 이동합니다.

참조 솔루션

CJam의 기본 참조 솔루션은 다음과 같습니다.

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

여기에서 전체 테스트 스위트에 대해 실행할 수 있습니다. 점수는 다음과 같습니다.

1233
5240
4223
11110
7735
10497
11524
11392
Total: 62954

가능한 가장 간단한 방법입니다. 각 문자의 코드 포인트를 리터럴로 푸시 한 다음 인쇄하십시오. 연속 문자, 정수 인쇄, 문자열의 반복 부분 등의 작은 차이는 사용하지 않습니다.

개선의 여지가 많다고 생각합니다. 참고로, 가장 짧은 수 제작 "Hello, World!" 길이는 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를 통해 한 문자에서 다른 문자로 전환하는 최적의 방법을 찾습니다. 나중에 변경 될 것이지만 숫자는 현재 즉시 푸시되어 인쇄됩니다.

나중에 재사용하기 위해 스택에 몇 가지 요소를 남겨 두는 가장 긴 공통 하위 시퀀스도 있습니다. 반복만큼 좋지는 않지만 적절한 절감 효과를 제공합니다.


Hooray, 누군가 싸울 사람 :-) 물론 지금은 갈 길이 멀다 ...
ETHproductions

5

자바 스크립트, 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

제 생각에는 좋은 출발이지만 분명히 끝나지 않았습니다. 각 문자를 개별적으로 만드는 대신 이전 문자 코드에서 더하거나 뺍니다. 메타 골프를 마쳤을 때 자세한 설명을 추가하겠습니다.


그래도 Chrome은 여전히에 대해 불평하지만 Firefox에서 작동합니다 charCodeAt.
Martin Ender
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.