N-queen-equine quine


21

よく知られているNクイーンの問題には、クイーンとナイトが関係し、「かなり難しい」 と言われている変種があります1。問題のステートメントは次のとおりです。

駒が他の駒を攻撃しないように、同じ数の騎士♞と女王♛をチェス盤に配置する必要があります。ボードに配置できるピースの最大数はいくつですか?また、いくつの方法でそれを行うことができますか?

このチャレンジでは、3〜32の入力nが(言語に最適な方法で)与えられます。与えられたnに対して、上記の問題に対するゼロ以上の解があるかもしれません。解決策がない場合は、何も出力/返さない必要があります(nil空の文字列false、...)。それ以外の場合、2つの結果を与える必要があります。

  1. サイズnのソリューションボード(以下を参照)。クイーンまたはナイトのチェスの駒を、攻撃を受けている駒なしで追加することはできません。同数の女王と騎士がいるに違いない
  2. プログラムのソースには入力を受け付けず、(I)が得られる実行する別の溶液(または何も同じサイズ)のn次の溶液のための(ii)の他のプログラムと同様に、同じ形式で、(など...)。

ご了承ください:

  • プログラムのシーケンスは、同じボードを2回返してはならず、サイズnの問題に対して考えられるすべてのソリューションをカバーし、最終的に終了する必要があります(出力は生成されません)。
  • 2つの値を返すか、1つを返してもう1つを印刷するか、2つの戻り値を印刷することができます。
  • ただし、ボードと次のプログラムの両方を印刷する場合、ボードを次のプログラムの一部とみなしてはなりません(コメントでボードを印刷するか、標準出力とエラーストリームの両方を使用することをお勧めします)。
  • 戻り値としてのプログラムは、クロージャではなく文字列でなければなりません。

ボードフォーマット

  • ボードは、サイズnの正方形です。
  • ボードセルは空、女王、または騎士にすることができます。
  • セルの種類ごとに異なる値を選択する必要があります(つまり、ボードの印刷時にQ、N以外の記号を使用できます)。
  • 文字列以外のボードを返す場合、ボードのn 2個の値の順序付きコレクションである必要があります(行列、ベクトル、または行/列優先順のリストなど)。
  • ボードを印刷する場合、正方形または線として印刷できます。たとえば、サイズ4のソリューションボードは、次のように印刷できます(スペースは不要です。シンボルは任意です)。

    Q - - -
    - - - -
    - - - -
    - - N -
    

    もしそうなら、出力することもできます:

    ♛ · · ·
    · · · ·
    · · · ·
    · · ♞ ·
    

    ...しかしこれで十分です:

    Q-------------N-
    

    対称的な解決策があるため、行優先順または列優先順でセルを反復するかどうかは関係ありません。たとえば、n = 4のソリューションは次のとおりです。

    Q------N--------
    Q----------N----
    Q------------N--
    Q-------------N-
    -Q----------N---
    -Q------------N-
    -Q-------------N
    --Q---------N---
    --Q----------N--
    --Q------------N
    ---QN-----------
    ---Q----N-------
    ---Q---------N--
    ---Q----------N-
    ---NQ-----------
    ----Q------N----
    ----Q----------N
    N------Q--------
    -------QN-------
    -------Q----N---
    ---N----Q-------
    -------NQ-------
    --------Q------N
    N----------Q----
    ----N------Q----
    -----------QN---
    -N----------Q---
    --N---------Q---
    -------N----Q---
    -----------NQ---
    N------------Q--
    --N----------Q--
    ---N---------Q--
    N-------------Q-
    -N------------Q-
    ---N----------Q-
    -N-------------Q
    --N------------Q
    ----N----------Q
    --------N------Q
    

n = 5の解を行列として見ることもできます。ボードには#、さまざまな種類の空のセルである、qおよびnシンボルが含まれています(以下の回答を参照)。私は数える2836枚のボードをするために、N = 6 Sleafarの答えのように、(バイト数を減らしたとき、私はバグを導入し、それが修正されました)。

私のコードで1つではなく2つのバグを見つけてくれたSleafarに感謝します。

スコア

バイト単位の最短コードが勝ちます。

nを受け入れる最初のプログラムのサイズを測定します。


1クイーンズアンドナイツ、ロジャーKWフイ(注意!ソリューションが含まれています)


4
たぶん、あなたはこれに賞金をかける必要があります。正直なところ、この問題はクインの部分がなければ十分に困難です。
mbomb007

Q、N、および-以外の記号を使用して、女王と騎士を表し、それらが明確である限り空にできますか?
16

@Fatalizeはい、確かに
coredump

1
@coredump関数の内容を読み取ることを意味しました。そして、私はそれを「はい、あなたはあなた自身のソースコードや関数の内容を読むことを許可されている」と考えます。(私のソリューションはそれに依存しているので...)
wizzwizz4

1
@coredumpチャレンジを正しく理解している場合、n = 6の参照ソリューションには無効なエントリが含まれています(たとえば-------------------------N--------Q-、ピースを追加できるため無効です:)Q--------N---------------N--------Q-
Sleafar

回答:


2

Groovy、515バイト

X=0;Y="N="+args[0]+";M=N*N;S=[];def f(b,i,j,v){(i..<j).findAll{k->!(0..<M).any{l->w=b[l];r=(k.intdiv(N)-l.intdiv(N)).abs();c=(k%N-l%N).abs();s=v+w;w>0&&(k==l||(r==0||c==0||r==c?s<4:r<3&&c<3&&s>2))}}.collect{a=b.clone();a[it]=v;[it,a]}};def r(b,q,n){f(b,q,M,1).each{i->f(i[1],n,M,2).each{j->if(f(j[1],0,M,1).any{f(it[1],0,M,2)}){r(j[1],i[0],j[0])}else{S.add(j[1])}}}};r(new int[M],0,0);if(x<S.size()){sprintf('//%s%cX=%d;Y=%c%s%c;print(Eval.xy(X,Y,Y))',S[x].toString(),10,x+1,34,y,34)}else{''}";print(Eval.xy(X,Y,Y))

テスト中

提供するn個のコマンドライン引数として:

groovy qak.groovy 4

出力の最初の行は常にコメント(0 =空、1 =女王、2 =騎士)としてのソリューションであり、2行目のコードが続きます。

//[1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]
X=1;Y="N=4;M=N*N;S=[];def f(b,i,j,v){(i..<j).findAll{k->!(0..<M).any{l->w=b[l];r=(k.intdiv(N)-l.intdiv(N)).abs();c=(k%N-l%N).abs();s=v+w;w>0&&(k==l||(r==0||c==0||r==c?s<4:r<3&&c<3&&s>2))}}.collect{a=b.clone();a[it]=v;[it,a]}};def r(b,q,n){f(b,q,M,1).each{i->f(i[1],n,M,2).each{j->if(f(j[1],0,M,1).any{f(it[1],0,M,2)}){r(j[1],i[0],j[0])}else{S.add(j[1])}}}};r(new int[M],0,0);if(x<S.size()){sprintf('//%s%cX=%d;Y=%c%s%c;print(Eval.xy(X,Y,Y))',S[x].toString(),10,x+1,34,y,34)}else{''}";print(Eval.xy(X,Y,Y))

次のスクリプトを自動テストに使用できます(nを引数として再度指定します)。

#!/bin/bash
set -e
test -n "$1"
groovy qak.groovy "$1" > t
while test -s t; do
    head -n1 t
    groovy t > t2
    mv t2 t
done

ソリューションをできる限り小さくしようとしたため、非常に時間がかかります(詳細については以下を参照)。このバージョンではn = 4のみをテストして、検証が機能するかどうかを確認しました。

結果

n = 4:40のソリューション変換された形式
n = 5: 172のソリューション変換された形式
n = 6: 2836のソリューション変換された形式

アルゴリズム

これは、ソリューションのやや未使用の非クインバージョンです。

N=args[0] as int
M=N*N
S=[]

/**
 * Generate a list of valid posibilities to place a new piece.
 * @param b Starting board.
 * @param i Start of the index range to check (inclusive).
 * @param j End of the index range to check (exclusive).
 * @param v Value of the new piece (1=queen, 2=knight).
 * @return A pair with the index of the new piece and a corresponding board for each possibility.
 */
def f(b,i,j,v){
    (i..<j).findAll{k->
        !(0..<M).any{l->
            w=b[l]
            r=(k.intdiv(N)-l.intdiv(N)).abs()
            c=(k%N-l%N).abs()
            s=v+w
            w>0&&(k==l||(r==0||c==0||r==c?s<4:r<3&&c<3&&s>2))
        }
    }.collect{
        a=b.clone();a[it]=v;[it,a]
    }
}

/**
 * Recursively look for solutions.
 * @param b Starting board.
 * @param q Start of the index range to check for queens.
 * @param n Start of the index range to check for knights.
 */
def r(b,q,n){
    f(b,q,M,1).each{i->
        f(i[1],n,M,2).each{j->
            if(f(j[1],0,M,1).any{f(it[1],0,M,2)}){
                r(j[1],i[0],j[0])
            }else{
                S.add(j[1])
            }
        }
    }
}

r(new int[M],0,0)
S.each{println(it)}

資格

ここでは、コードサイズを小さく保つために非常に単純なアプローチを使用しました。

X=0;Y="...";print(Eval.xy(X,Y,Y))

変数Xは、次に印刷するソリューションのインデックスを保持します。Yは上記のアルゴリズムの修正されたコピーを保持します。これは、すべての解を計算し、そのうちの1つだけを選択するために使用されます。これが非常に遅い理由です。このソリューションの利点は、追加のコードをあまり必要としないことです。Yに格納されたコードは、Evalクラスの助けを借りて実行されます(本当のクインは必要ありません)。

修正されたコードは、溶液により指さプリントX、増加Xをそれ自体のコピーを付加します。

//[...]
X=1;Y="...";print(Eval.xy(X,Y,Y))

また、すべてのソリューションを2番目のステップのコードとして出力しようとしましたが、n = 6の場合、Groovyが処理するには大量のコードが生成されていました。


良い答え、良い仕事です。
コアダンプ

6

Common Lisp、737

自己回答

(lambda(n &aux(d 1))#2=(catch'$(let((s(* n n))(c d))(labels((R(w % @ b ! &aux r h v a)(loop for u from % below s do(setf h(mod u n)v(floor u n)a #4=(aref b u))(when(< 0(logand a w)4)(and(= 6 w)!(throw'! t))(let((b(copy-seq b))(o 5))(loop for(K D)on'(-1 -2 -1 2 1 -2 1 2)for y =(+ K v)for x =(+(or D -1)h)for u =(and(< -1 y n)(< -1 x n)(+(* y n)x))if u do #1=(if(< #4#4)(setf #4#(logand #4#o(if(= w o)3 0)))))(#8=dotimes(y N)(#8#(x N)(let((u(+(* y n)x))(o 6))(if(or(= x h)(= y v)(=(abs(- h x))(abs(- v y))))#1#))))(setf #4#w r(or(cond((= w 5)(R 6 @ U b !))((R 5 @ U b())t)((catch'!(R 5 0 0 b t))t)(t(and(=(decf c)0)(incf d)(or(format t"~%(lambda(&aux(n ~A)(d ~A))~%~S)"n d'#2#)(throw'$ B)))t))r)))))r))(R 5 0 0(fill(make-array s)3)())))))

関数オブジェクトを返すREPLに上記を貼り付けます。

#<FUNCTION (LAMBDA (N &AUX (D 1))) {1006D1010B}>

それを呼び出します(星は最後に返された値にバインドされます):

QN> (funcall * 4)

これにより、以下が標準出力に出力されます。

(lambda(&aux(n 4)(d 2))
#1=(CATCH '$
 (LET ((S (* N N)) (C D))
   (LABELS ((R (W % @ B ! &AUX R H V A)
              (LOOP FOR U FROM % BELOW S
                    DO (SETF H (MOD U N)
                             V (FLOOR U N)
                             A #2=(AREF B U)) (WHEN (< 0 (LOGAND A W) 4)
                                                (AND (= 6 W) !
                                                     (THROW '! T))
                                                (LET ((B (COPY-SEQ B))
                                                      (O 5))
                                                  (LOOP FOR (K D) ON '(-1
                                                                       -2
                                                                       -1 2
                                                                       1 -2
                                                                       1 2)
                                                        FOR Y = (+ K V)
                                                        FOR X = (+
                                                                 (OR D -1)
                                                                 H)
                                                        FOR U = (AND
                                                                 (< -1 Y N)
                                                                 (< -1 X N)
                                                                 (+ (* Y N)
                                                                    X))
                                                        IF U
                                                        DO #3=(IF (< #2# 4)
                                                                  (SETF #2#
                                                                          (LOGAND
                                                                           #2#
                                                                           O
                                                                           (IF (=
                                                                                W
                                                                                O)
                                                                               3
                                                                               0)))))
                                                  (DOTIMES (Y N)
                                                    (DOTIMES (X N)
                                                      (LET ((U
                                                             (+ (* Y N) X))
                                                            (O 6))
                                                        (IF (OR (= X H)
                                                                (= Y V)
                                                                (=
                                                                 (ABS
                                                                  (- H X))
                                                                 (ABS
                                                                  (- V
                                                                     Y))))
                                                            #3#))))
                                                  (SETF #2# W
                                                        R
                                                          (OR
                                                           (COND
                                                            ((= W 5)
                                                             (R 6 @ U B !))
                                                            ((R 5 @ U B
                                                                NIL)
                                                             T)
                                                            ((CATCH '!
                                                               (R 5 0 0 B
                                                                  T))
                                                             T)
                                                            (T
                                                             (AND
                                                              (= (DECF C)
                                                                 0)
                                                              (INCF D)
                                                              (OR
                                                               (FORMAT T
                                                                       "~%(lambda(&aux(n ~A)(d ~A))~%~S)"
                                                                       N D
                                                                       '#1#)
                                                               (THROW '$
                                                                 B)))
                                                             T))
                                                           R)))))
              R))
     (R 5 0 0 (FILL (MAKE-ARRAY S) 3) NIL)))))

また、この関数によって返される値は次のとおりです。

#(5 0 0 0 0 0 0 6 0 0 0 2 0 2 0 0)

...これは配列リテラルです。番号5は女王を表し、6は騎士を表し、その他は空のセルを表します。ただし、内部に格納されている情報がさらにある場合を除きます。返された関数をコピーしてreplに貼り付けると、新しい関数が取得されます。

#<FUNCTION (LAMBDA (&AUX (N 4) (D 2))) {100819148B}>

そして、引数なしでそれを呼び出すことができます:

QN> (funcall * )

この呼び出しは、新しいソリューション#(5 0 0 0 0 0 0 2 0 0 0 6 0 0 2 0)と別の関数のソースを返します(ここには表示されていません)。元の関数または最後に生成された関数が解を見つけられない場合、何も出力されず、何も返されません。

内部値

|----------+--------+---------+--------+-----------------|
|          | Binary | Decimal | Symbol | Meaning         |
|----------+--------+---------+--------+-----------------|
| Empty    |    000 |       0 | -      | safe for none   |
|          |    001 |       1 | q      | safe for queen  |
|          |    010 |       2 | n      | safe for knight |
|          |    011 |       3 | #      | safe for both   |
|----------+--------+---------+--------+-----------------|
| Occupied |    101 |       5 | Q      | a queen         |
|          |    110 |       6 | K      | a knight        |
|----------+--------+---------+--------+-----------------|

生成するソリューションが少なすぎました。さて、女王と騎士にとって安全なセルを独立して伝播します。たとえば、n = 5の場合のプリティプリンティングの出力は次のとおりです。

Q - - - - 
- - - n N 
- q - n n 
- # n - n 
- n # # - 

私たちが女王を置いたときQ、この女王から騎士が離れた位置は女王にとってまだ安全であり、示されていqます。同様に、女王のみが到達可能なナイトは他のナイトにとって安全です。値はビット単位であり、可能な動きを表すために-edされ、一部のセルは種類なしで到達可能です。

より正確には、次のソリューション(左から右)につながるボードのシーケンスがあります。ここでは、フリーセルが異なる値で徐々に制約されます。

# # # # # #     q - - - q #     - - - - - #     - - - - - #     - - - - - n
# # # # # #     - - Q - - -     - - Q - - -     - - Q - - -     - - Q - - -
# # # # # #     q - - - q #     q - - - - -     Q - - - - -     Q - - - - -
# # # # # #     - q - q - #     - q - - - n     - - - - - n     - - - - - n
# # # # # #     # # - # # -     n n - n N -     - - - n N -     - - - - N -
# # # # # #     # # - # # #     # # - n n n     - # - - n n     - n - - n N

ノンクインアプローチ

Ungolfed、コメント付きバージョン

(defun queens-and-knights
    (n    ; size of problem
     fn   ; function called for each solution

     ;; AUX parameters are like LET* bindings but shorter.
     &aux
       ;; total number of cells in a board
       (s (* n n)))

  (labels
      ;; Define recursive function R
      ((R (w      ; what piece to place: 5=queen, 6=knight 
           %      ; min position for piece of type W
           @      ; min position for the other kind of piece
           b      ; current board
           !      ; T iff we are in "check" mode (see below)
           &aux  
           r      ; result of this function: will be "true" iff we can
                  ; place at least one piece of type W on the board b
           h      ; current horizontal position 
           v      ; current vertical position
           a      ; current piece at position (h,v)
           )

         (loop
            ;; only consider position U starting from position %,
            ;; because any other position below % was already visited
            ;; at a higher level of recursion (e.g. the second queen
            ;; we place is being placed in a recursive call, and we
            ;; don't visit position before the first queen).
            for u from % below s

            do
              (setf h (mod u n)         ; Intialize H, V and A
                    v (floor u n)       ; 
                    a (aref b u))       ; 

            ;; Apply an AND mask to current value A in the board
            ;; with the type of chess piece W. In order to consider
            ;; position U as "safe", the result of the bitwise AND
            ;; must be below 4 (empty cell) and non-null.
              (when (< 0 (logand a w) 4)

                ;; WE FOUND A SAFE PLACE TO PUT PIECE W

                (when (and ! (= 6 w))
                  ;; In "check" mode, when we place a knight, we knwo
                  ;; that the check is successful. In other words, it
                  ;; is possible to place an additional queen and
                  ;; knight in some board up the call stack. Instead
                  ;; of updating the board we can directly exit from
                  ;; here (that gave a major speed improvement since
                  ;; we do this a lot). Here we do a non-local exit to
                  ;; the catch named "!".
                  (throw '! t))

                ;; We make a copy of current board b and bind it to the
                ;; same symbol b. This allocates a lot of memory
                ;; compared to the previous approach where I used a
                ;; single board and an "undo" list, but it is shorter
                ;; both in code size and in runtime.
                (let ((b (copy-seq b)))

                  ;; Propagate knights' constraints
                  (loop
                     ;; O is the other kind of piece, i.e. queen here
                     ;; because be propagate knights. This is used as
                     ;; a mask to remove knights pieces as possible
                     ;; choices.
                     with o = 5

                     ;; The list below is arranged so that two
                     ;; consecutive numbers form a knight-move. The ON
                     ;; iteration keyword descend sublist by sublist,
                     ;; i.e. (-1 -2), (-2 -1), (-1 2), ..., (2 NIL). We
                     ;; destructure each list being iterated as (K D),
                     ;; and when D is NIL, we use value -1.
                     for (K D) on '(-1 -2 -1 2 1 -2 1 2)

                     ;; Compute position X, Y and index U in board,
                     ;; while checking that the position is inside the
                     ;; board.
                     for y = (+ K v)
                     for x = (+ (or D -1) h)
                     for u = (and (< -1 y n)
                                  (< -1 x n)
                                  (+(* y n)x))

                     ;; if U is a valid position...
                     if u
                     do
                     ;; The reader variable #1# is affected to the
                     ;; following expression and reused below for
                     ;; queens. That's why the expression is not
                     ;; specific to knights. The trick here is to
                     ;; use the symbols with different lexical
                     ;; bindings.
                       #1=(when (< (aref b u) 4) ; empty?
                            (setf (aref b u)

                                  (logand
                                   ;; Bitwise AND of current value ...
                                   (aref b u)

                                   ;; ... with o: position U is not a
                                   ;; safe place for W (inverse of O)
                                   ;; anymore, because if we put a W
                                   ;; there, it would attack our
                                   ;; current cell (H,V).
                                   o

                                   ;; ... and with zero (unsafe for
                                   ;; all) if our piece W is also a
                                   ;; knight (resp. queen). Indeed, we
                                   ;; cannot put anything at position
                                   ;; U because we are attacking it.
                                   (if (= w o) 3 0)))))

                  ;; Propagate queens' constraints
                  (dotimes (y N)
                    (dotimes (x N)
                      (let ((u(+(* y n)x))(o 6))
                        (if (or (= x h)
                                (= y v)
                                (= (abs(- h x)) (abs(- v y))))

                            ;; Same code as above #1=(if ...)
                            #1#))))

                  (setf
                   ;; Place piece
                   (aref b u) w

                   ;; Set result value
                   r (or (cond
                           ;; Queen? Try to place a Knight and maybe
                           ;; other queens. The result is true only if
                           ;; the recursive call is.
                           ((= w 5) (R 6 @ U b !))

                           ;; Not a queen, so all below concern   
                           ;; knights: we always return T because
                           ;; we found a safe position.
                           ;; But we still need to know if
                           ;; board B is an actual solution and 
                           ;; call FN if it is.
                           ;; ------------------------------------

                           ;; Can be place a queen too? then current
                           ;; board is not a solution.
                           ((R 5 @ U b()) t)

                           ;; Try to place a queen and a knight
                           ;; without constraining the min positions
                           ;; (% and @); this is the "check" mode that
                           ;; is represented by the last argument to
                           ;; R, set to T here. If it throws true,
                           ;; then board B is a duplicate of a
                           ;; previous one, except that it is missing
                           ;; pieces due to constraints % and @. The
                           ;; "check" mode is a fix to a bug where we
                           ;; reported as solutions boards where there
                           ;; was still room for other pieces.
                           ((catch'!(R 5 0 0 b t)) t)

                           ;; Default case: we could not add one more
                           ;; layer of pieces, and so current board B
                           ;; is a solution. Call function FN.
                           (t (funcall fn b) t))

                         ;; R keeps being true if it already was for
                         ;; another position.
                         r)))))

         ;; Return result R
         r))

    ;; Start search with a queen and an empty board.
    (R 5 0 0 (fill (make-array s) 3)  nil)))

重複とバグ

私の最初のソリューションは重複したソリューションを出力しました。それを解決するために、私は女王と騎士のために2つのカウンターを導入しました。女王(または騎士)のカウンターは、女王(または騎士)が存在するボードの最初の位置を追跡します。女王(または騎士)は、その最小位置に続く位置にのみ追加します。

その方法は、クイーン(またはナイト)の位置を増やして反復するため、以前の反復で既に見つかったソリューションを再訪することを防ぎます。

しかし、Sleafarは、ルールに反して、女王と騎士のための余地がある解決策があることに気づきました。しばらくの間、私は通常の検索に戻して、重複を防ぐために既知のソリューションをすべて保存しなければなりませんでしたが、これはコストがかかりすぎました(バイトとメモリの両方の面で)。

代わりに、ここで私は今何をすべきかです:潜在的なソリューション・ボードが発見された場合、私は正確に追加しよう1人の女王と1人のアカウントにカウンター(つまり、ボード上のすべてのセルの)を取ることなく、騎士。これが可能な場合、現在のボードは前のボードの複製であり、解決策を拒否します。

テスト

|---+---------+------------+--------------|
| N |  boards |    seconds |        bytes |
|---+---------+------------+--------------|
| 3 |       0 |          0 |        32768 |
| 4 |      40 |          0 |       360416 |
| 5 |     172 |          0 |      3440016 |
| 6 |    2836 |   0.085907 |     61251584 |
| 7 |   23876 |   1.265178 |    869666288 |
| 8 |  383586 |  24.991300 |  17235142848 |
| 9 | 6064506 | 524.982987 | 359952648832 |
|---+---------+------------+--------------|

クイン化

連続したクインを作るためのさまざまなアイデアがありました。最も簡単な方法は、最初にすべてのソリューションを文字列のリストとして最初に生成し、各世代でそのリストからポップする順次クインを記述することです。しかし、これは現在のアプローチよりも短いようには見えませんでした。または、カスタムスタックを使用して再帰コードを書き直し、解決策が見つかるたびにすべての状態変数をダンプしようとしました。目標は、次のステップを現在のステップの継続として処理できることです。たぶん、これはスタックベースの言語により適しています。現在のものは非常に単純であり、Common Lispリーダー変数に依存しています。これは常に使用するのが楽しいです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.