Kody QR… i cały ten jazz!

18

Będzie to stosunkowo trudne wyzwanie dla golfisty.

Dane wejściowe: dowolny adres URL musi mieć dołączony protokół, np. Http://codegolf.stackexchange.com (który będzie naszym przypadkiem testowym)

Dane wyjściowe: Wygenerowany kod QR reprezentujący ten adres URL, który po zeskanowaniu przez urządzenie inteligentne przeniesie Cię pod ten adres URL w przeglądarce urządzenia inteligentnego.

Zasady tego Code-Golfa

  1. Jak zwykle wygrywa najmniejszy kod.
  2. Brak zewnętrznych zasobów internetowych, bibliotek lub wtyczek do generowania kodu dla Ciebie. Twój kod musi obliczyć obraz kodu QR.
  3. Dane wyjściowe mogą być prezentowane przez obraz, wygenerowany przez HTML5 / CSS3, lub nawet przy użyciu odpowiednich bloków Unicode, lub jeśli ASCII twojej platformy ma to dostępne, poprzez znaki ASCII, które mogą tworzyć kod QR (ten ostatni jest skierowany do Commodore 64 Basic, Amiga QBasic, Amstrad Basic itp.), Ale musi wygenerować kod QR, aby można było zeskanować kod.
  4. Po wpisaniu kodu należy wygenerować dane wyjściowe, albo zrzut ekranu wyniku po wykonaniu kodu, albo link pokazujący dane wyjściowe (w zależności od tego, co najlepiej pasuje do sytuacji)
  5. Masz przetestować swój kod za pomocą adresu URL „ http://codegolf.stackexchange.com ” i zgłosić wynik zgodnie z Regułami 3–4.
  6. Musisz również przetestować kod z wybranym adresem URL i zgłosić wynik zgodnie z Regułami 3–4.

Bibliografia:

1) http://en.wikiversity.org/wiki/Reed%E2%80%93Solomon_codes_for_coders

2) http://www.pclviewer.com/rs2/calculator.html

3) http://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction

4) http://en.wikipedia.org/wiki/QR_code

5) http://www.qrstuff.com/ po inspirację ...;)

WallyWest
źródło
4
Nieskończona rekurencja przy regule 5.
użytkownik12205
@ace Dobrze zauważony ... to zostało naprawione
WallyWest
1
Po przeczytaniu dokumentacji uważam, że „stosunkowo trudne” to mało powiedziane.
Danny
Czy możemy uzyskać wyjaśnienie, co oznacza „Twój kod musi obliczyć obraz kodu QR”? Biorę to oznaczać, że musi zrobić dwa główne punkty w przedłożonej kodu : 1) kodowania RS , 2) układ modułu .
Nick T
reguła 3: czy więc grafika ascii jest drukowana z terminala grzywny, czy też musi przejść do rzeczywistego pliku obrazu?

Odpowiedzi:

17

Znaki Pythona 3: 974 [nb]

Dalsze bicie brzydkim kijem, patrz notatnik na GH-Gist . Python 3 ma wbudowane kodowanie ASCII-85, które pomaga w spakowaniu kiełbasy. Bardziej zaawansowane wbudowane algorytmy kompresji (LZMA) 3 nie wydają się dobrze współpracować z tak małymi rzeczami.

Zipowanie jest bardzo kapryśne w zmienianiu znaków, prawie kusiło mnie, aby napisać coś, co losowo wypróbuje różne 1-literowe nazwy zmiennych, aby zminimalizować spakowany rozmiar.

Python 2: 1420 1356 1085 1077 znaków

wprowadź opis zdjęcia tutaj

Przeczytałem pierwszy argument przekazany po wywołaniu, który może być łańcuchem o długości do 106 znaków. Wyjście jest zawsze wersją 5-L kodu QR i maski 4, co oznacza, że ​​ma wymiary 37 x 37 modułów i może obsłużyć tylko ~ 5% uszkodzeń.

Jedynymi zależnościami programu są numpy(manipulacje tablicami) i matplotlib(tylko wyświetlanie); całe kodowanie Reed-Solomon, pakowanie danych i układ modułu są obsługiwane w ramach dostarczonego kodu . W przypadku RS w zasadzie obrabowałem funkcje Wikiversity . To wciąż dla mnie rodzaj czarnej skrzynki. Nauczyłem się ton o QR w każdym przypadku.

Oto kod, zanim go pokonam brzydkim kijem:

import sys
import numpy as np
import matplotlib.pyplot as plt
# version 5-L ! = 108 data code words (bytes), 106 after metadata/packing

### RS code stolen from https://en.wikiversity.org/wiki/Reed%E2%80%93Solomon_codes_for_coders#RS_generator_polynomial
gf_exp = [1] + [0] * 511
gf_log = [0] * 256
x = 1
for i in range(1,255):
    x <<= 1
    if x & 0x100:
        x ^= 0x11d
    gf_exp[i] = x
    gf_log[x] = i
for i in range(255,512):
    gf_exp[i] = gf_exp[i-255]

def gf_mul(x,y):
    if x==0 or y==0:
        return 0
    return gf_exp[gf_log[x] + gf_log[y]]

def main():
    s = sys.argv[1]

    version = 5
    mode = 4 # byte mode
    dim = 17 + 4 * version
    datamatrix = 0.5 * np.ones((dim, dim))
    nsym = 26

    # PACK
    msg = [mode * 16, len(s) * 16] + [ord(c) << 4 for c in s]
    for i in range(1, len(msg)):
        msg[i-1] += msg[i] // 256
        msg[i] = msg[i] % 256

    pad = [236, 17]
    msg = (msg + pad * 54)[:108]

    # MAGIC (encoding)
    gen = [1]
    for i in range(0, nsym):
        q = [1, gf_exp[i]]
        r = [0] * (len(gen)+len(q)-1)
        for j in range(0, len(q)):
            for i in range(0, len(gen)):
                r[i+j] ^= gf_mul(gen[i], q[j])
        gen = r
    msg_enc = [0] * (len(msg) + nsym)
    for i in range(0, len(msg)):
        msg_enc[i] = msg[i]
    for i in range(0, len(msg)):
        coef = msg_enc[i]
        if coef != 0:
            for j in range(0, len(gen)):
                msg_enc[i+j] ^= gf_mul(gen[j], coef)
    for i in range(0, len(msg)):
        msg_enc[i] = msg[i]


    # PATTERN
    # position marks
    for _ in range(3):
        datamatrix = np.rot90(datamatrix)
        for i in range(4):
            datamatrix[max(0, i-1):8-i, max(0, i-1):8-i] = i%2
    datamatrix = np.rot90(datamatrix.T)

    # alignment
    for i in range(3):
        datamatrix[28+i:33-i, 28+i:33-i] = (i+1)%2

    # timing
    for i in range(7, dim-7):
        datamatrix[i, 6] = datamatrix[6, i] = (i+1)%2

    # the "dark module"
    datamatrix[dim-8, 8] = 1

    # FORMAT INFO
    L4 = '110011000101111' # Low/Mask4
    ptr_ul = np.array([8, -1])
    steps_ul = [0, 1] * 8 + [-1, 0] * 7
    steps_ul[13] = 2 # hop over vertical timing
    steps_ul[18] = -2 # then horizontal

    ptr_x = np.array([dim, 8])
    steps_x = [-1, 0] * 7 + [15-dim, dim-16] + [0, 1] * 7

    for bit, step_ul, step_x in zip(L4, np.array(steps_ul).reshape(-1,2), np.array(steps_x).reshape(-1,2)):
        ptr_ul += step_ul
        ptr_x += step_x
        datamatrix[tuple(ptr_ul)] = int(bit)
        datamatrix[tuple(ptr_x)] = int(bit)

    # FILL
    dmask = datamatrix == 0.5

    cols = (dim-1)/2
    cursor = np.array([dim-1, dim]) # starting off the matrix
    up_col = [-1, 1, 0, -1] * dim
    down_col = [1, 1, 0, -1] * dim
    steps = ([0, -1] + up_col[2:] + [0, -1] + down_col[2:]) * (cols/2)
    steps = np.array(steps).reshape(-1, 2)
    steps = iter(steps)

    # bit-ify everything
    msg_enc = ''.join('{:08b}'.format(x) for x in msg_enc) + '0' * 7 # 7 0's are for padding
    for bit in msg_enc:
        collision = 'maybe'
        while collision:
            cursor += steps.next()
            # skip vertical timing
            if cursor[1] == 6:
                cursor[1] = 5
            collision = not dmask[tuple(cursor)]
        datamatrix[tuple(cursor)] = int(bit)

    # COOK
    mask4 = lambda i, j: (i//2 + j//3)%2 == 0
    for i in range(dim):
        for j in range(dim):
            if dmask[i, j]:
                datamatrix[i, j] = int(datamatrix[i, j]) ^ (1 if mask4(i, j) else 0)

    # THE PRESTIGE
    plt.figure(facecolor='white')
    plt.imshow(datamatrix, cmap=plt.cm.gray_r, interpolation='nearest')
    plt.axis('off')
    plt.show()

if __name__ == '__main__':
    main()

Po:

import sys
from pylab import*
n=range
l=len
E=[1]+[0]*511
L=[0]*256
x=1
for i in n(1,255):
 x<<=1
 if x&256:x^=285
 E[i]=x;L[x]=i
for i in n(255,512):E[i]=E[i-255]
def f(x,y):
 if x*y==0:return 0
 return E[L[x]+L[y]]
m=sys.argv[1]
m=[ord(c)*16 for c in'\4'+chr(l(m))+m]
for i in n(1,l(m)):m[i-1]+=m[i]/256;m[i]=m[i]%256
m=(m+[236,17]*54)[:108]
g=[1]
for i in n(26):
 q=[1,E[i]]
 r=[0]*(l(g)+l(q)-1)
 for j in n(l(q)):
    for i in n(l(g)):r[i+j]^=f(g[i],q[j])
 g=r
e=[0]*134
for i in n(108):
 e[i]=m[i]
for i in n(108):
 c=e[i]
 if c: 
    for j in n(l(g)):e[i+j]^=f(g[j],c)
for i in n(108):e[i]=m[i]
m=.1*ones((37,)*2)
for _ in n(3):
 m=rot90(m)
 for i in n(4):m[max(0,i-1):8-i,max(0,i-1):8-i]=i%2
m=rot90(m.T)
for i in n(3):m[28+i:33-i,28+i:33-i]=(i+1)%2
for i in n(7,30):m[i,6]=m[6,i]=(i+1)%2
m[29,8]=1
a=array
t=tuple
g=int
r=lambda x:iter(a(x).reshape(-1,2))
p=a([8,-1])
s=[0,1]*8+[-1,0]*7
s[13]=2
s[18]=-2
P=a([37,8])
S=[-1,0]*7+[-22,21]+[0,1]*7
for b,q,Q in zip(bin(32170)[2:],r(s),r(S)):p+=q;P+=Q;m[t(p)]=g(b);m[t(P)]=g(b)
D=m==0.1
c=a([36,37])
s=r(([0,-1]+([-1,1,0,-1]*37)[2:]+[0,-1]+([1,1,0,-1]*37)[2:])*9)
for b in ''.join('{:08b}'.format(x) for x in e):
 k=3
 while k:
    c+=s.next()
    if c[1]==6:c[1]=5
    k=not D[t(c)]
 m[t(c)]=g(b)
a=n(37)
for i in a:
 for j in a:
    if D[i,j]:m[i,j]=g(m[i,j])^(j%3==0)
imshow(m,cmap=cm.gray_r);show()

(polegając na tabulatorze, który liczy się jako 4/8 / dowolna liczba spacji> = 2., nie jestem pewien, jak dobrze się skopiuje)

Ponieważ jest tak długi, możemy go skompresować (zobaczyłem, że ktoś robi to gdzieś indziej, zapomniałem, kto jednak :(), aby zapisać więcej znaków, zmniejszając całkowitą liczbę do 1085 1077, ponieważ pylabjest brudny:

import zlib,base64
exec zlib.decompress(base64.b64decode('eJxtU0tzmzAQvvSkX6FLaglkyiM2hHRvyS2HZNobo3QwwY6IBVjQFrfT/96V3KR4Wg5I+/6+3ZXSfWdGOhwHsjWdpv1xX26oclqPtGDKdleTPezrltxCEUm/CKW3iiJyB/YWr9ZkgohsO0MVVS1tWSTi1YrnhE4fP6KFqi2d3qNfPj1CnK0IvS2UhOn6rpgkqHkkxolVFPPceeBviRpJnuot3bJJHG1Sm807AoS5qcevpqUhoX9ut4VN6d8VRymJBuQUlGb3DUGjVHTmiVXci9bUVqyw4uLdwq+eDdszzbmv5TkJp801gkDSgKf8gCSu7cVJF5a6Bqb9Ik7WIkqxLZe8yKMwk2RnW3VGbW3BH1AtLDmJoF3/sPiO+3t24MuIEwetOUVYnY3Bb5bHuvPcFMpv5CNs2Q6TiUPRSAzegSG1yxoll2dkwsxmql+h/8dWgbW69lY5favazKvWs6qNFBX/J8/fChqCyOvaemAsSQX34pPzl5NzYktqMN14FWKbyZzhpW26LicWCmw9z7OlEucibs1FTN7Cg89nQBIbH2e+ypMEQ99uEpjyI46RM+dUJKEbslhb4Gsxc8MsVyKTuMIllMaURzLC+LXf1zhd1Y7EwL7Um6eSTrkaa8NKNvHA1MNz2ddsia+Ac9JDyYpM4ApxMuBoRCS9zC/QilNKyVBEiYTYnlhoGZN7648Ny9D/E7z6YUAci9g9PpshdRQ24iAeLI0fqmcbhczjKA15EedSGDZw/H3CqfU+HK7vfXjA1R1ZzyXs2IY74f6PQG5A44sKIlK5+muRpA6wYQwr2gfALBZEYwUvSV0V/832j4l7V6ehbCzAxSJoOgS4+JmH2ebXIkCLLkfslxv8ZH1quxIvkBD6/Vnta/pyWv3KhyFo62lk3Ml2P/FpAaxzd66c9gXabqQ3SKniuMT6dDlxKwE7k85WpMxn76zMX9Pe4BI00u1CY0NPF/7ImosEm8OJ0sNz951pUemyh0oHO9yJL4ZfOzX/DQ2mdSs='))

wprowadź opis zdjęcia tutaj

Jeśli zamienisz ostatni wiersz na następujący (dodaje 62 znaki), otrzymasz prawie idealną wydajność, ale pozostałe nadal skanują, więc cokolwiek.

figure(facecolor='white');imshow(m,cmap=cm.gray_r,interpolation='nearest');axis('off');show()

Dobry kod QR

Nick T.
źródło
Dobra robota! Szkoda, że ​​Python nie jest najlepszy w golfowych rozwiązaniach, ale jest to niezwykłe kodowanie, @NickT!
WallyWest,
Prawdopodobnie mógłbym zaoszczędzić trochę więcej, jeśli stracę structpołączenie i trochę niepotrzebnego przesuwania, po prostu obcinając mój „ciąg główny” ...
Nick T
FYI drugi poziom wcięcia może wynosić tylko dwie spacje. Zauważyłem, że używasz czterech / tab.
Beta Decay
1
@BetaDecay ma to być tylko 1 tab (1 tab> 1 spacja jeśli chodzi o wcięcie ... Myślę, że SE łamie tabulatory?)
Nick T
@NickT tak, to robi.
Rɪᴋᴇʀ