星空のメタゴルフ


25

Starryは、難解なプログラミング言語です。コードは+*.,`'、各文字が表す実際のコマンドがその前のスペースの数によって決定される場所のみで構成されます。さまざまなコマンドが非常にさまざまなバイト数に対応できるため、ゴルフの固定出力の課題であっても難しいことです。特に、数値リテラルには単項表現があるため、小さい数字を操作して大きい数字を作成する必要があります。

したがって、この課題は、そのような星空プログラムをゴルフできるプログラムを書くことです。

Starryはどのように機能しますか?

(いくつかの詳細はエソランで未指定のままになっているため、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プログラムの合計バイト数です。同点の場合、最短のメタゴルファが勝ちます。つまり、同点がない限り、自分のコードをわざわざわざわざしないでください(最適な解決策が可能な場合にのみ起こると思います)。

以下にリストされている特定のテストケースに向けてコードを最適化しないでください。具体的には、手作りのソリューションをハードコーディングしないでください。与えられた文字列の構造に類似した構造を持つ文字列のクラスに最適化することは問題ありません。ハードコーディングソリューションの疑いがある場合、テストケースの一部またはすべてを(同等の構造の文字列で)置き換える権利を留保します。

テストケース

各行は個別のテストケースです。

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

2番目のテストケースのクレジットはDennisに送られます。4番目のテストケースのクレジットは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主なアルゴリズムです。1つの番号を受け取り、それをスタックに配置する最短の方法を見つけるか、2つの番号を取り、最初の番号が既にオンになっていると想定して2番目の番号をスタックに追加するコードを返します。$sこれらの操作の結果をさらに使用するために保持するハッシュです。

starry文字列を取得し、文字コードの配列(またはダイジェスト用の数字)に分割します。スタックの一番下の1つの番号でコードを開始します。次に、連続する各番号を生成できる最短の方法を計算します。最後の番号をコピーするか、最初にスタックに置かれた番号を使用する可能性があります。


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)

関連する関数は、適切な名前metagolfです。

結果は次のとおりです。

210
676
684
2007
1463
2071
2204
2530
Total: 11845

ここで完全な出力を見つけることができます。

簡単な説明

まだ改善すべきことがたくさんあるので、説明を簡潔にします。

基本的なアルゴリズムは、単に文字のペアを調べて、BFSを介してある文字から別の文字に遷移する最適な方法を見つけます。現在、数字はすぐにプッシュおよび印刷されますが、これは後で変更されます。

また、スタックにいくつかの要素を残して、後で再利用するために、少し長い共通サブシーケンスが実行されます。繰り返しほどではありませんが、かなりの節約になります。


万歳、戦いに誰か:-)もちろん、今私は私が持っていることがわかり、長い ...移動するための方法を
ETHproductions

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

私の意見では良いスタートですが、明らかに終了していません。各文字を個別に作成する代わりに、前の文字コードに加算または減算します。メタゴルフが終わったら、完全な説明を追加します。


はい、Firefoxで動作しますが、Chromeはまだに文句を言いcharCodeAtます。
マーティンエンダー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.