Это правильный шахматный ход?


15

Альтернативное имя: ChessMoveQ

Учитывая список до 32 элементов, каждый из которых состоит из 4 элементов, и второй список с 4 элементами, определите, является ли ход, описанный во втором входе, допустимым ходом шахмат.

Первый список указывает положение всех 32 фигур на доске. Каждый элемент будет следовать структуре <colour>, <piece-name>, <x-coord>, <y-coord>, например ["W", "K", 5, 1], которая указывает, что белый король включен 5, 1( e1на обычной шахматной доске). Все элементы первого ввода будут уникальными. <x-coord>и <y-coord>всегда будет между 1 и 8. Один из примеров:

[["B", "K", 3, 8], ["B", "Q", 1, 5], ["B", "N", 4, 7], ["B", "N", 7, 8],
 ["B", "B", 2, 4], ["B", "R", 4, 8], ["B", "R", 8, 8], ["B", "P", 1, 7],
 ["B", "P", 2, 7], ["B", "P", 3, 6], ["B", "P", 5, 6], ["B", "P", 6, 7],
 ["B", "P", 7, 7], ["B", "P", 8, 7], ["W", "K", 5, 1], ["W", "Q", 6, 3],
 ["W", "N", 3, 3], ["W", "B", 5, 2], ["W", "B", 6, 4], ["W", "R", 1, 1],
 ["W", "R", 8, 1], ["W", "P", 1, 3], ["W", "P", 2, 2], ["W", "P", 3, 2],
 ["W", "P", 4, 4], ["W", "P", 6, 2], ["W", "P", 7, 2], ["W", "P", 8, 3]]

который будет представлять доску:

пример шахматной доски

Второй вход будет состоять из тех же структур, что и подсписки первого, но вместо координат x и y, указывающих, где находится фрагмент, они указывают, куда он пытается переместиться.

В приведенном выше примере допустимым может быть движение ["W", "B", 4, 3](слон передвигается на одну клетку вперед и влево), а недопустимым может быть то, ["B", "R", 4, 1]что ладья должна пройти через коня и пешку, чтобы добраться до квадрата. Поскольку ход может относиться к нескольким фигурам время от времени, вы должны проверить, может ли какой-либо из указанных кусков сделать ход, а не только один из них. Например, первый пример действителен только для одного слона, но это все еще действительный ход. Однако ни одна черная ладья не может выполнить второй ход, поэтому она недействительна.

Ваша задача - определить, является ли ход, описанный во втором входе, допустимым ходом в шахматах. Срок действия правила варьируется в зависимости от фигуры, которая пытается переместиться (нажмите на название фигуры для диаграммы действительных ходов):

  • Любая фигура : Никакие фигуры не могут перемещаться на уже занятый квадрат или с доски, если только этот квадрат не занят частью другого цвета. Например, белая фигура может переместиться на квадрат, занятый черной фигурой, но не белая фигура. Кроме того, никакие фигуры, кроме Рыцарей, не могут перемещаться на квадраты, которые непосредственно закрыты другим куском.
    • Движение по частям B квадратного C является «непосредственно препятствуют» по частям А , если непосредственно, в прямом (ортогонально или диагонали) линии, между B и C .
  • Любая фигура : позиция короля также может влиять на достоверность хода фигуры. Если любое из этих двух условий выполнено, перемещение недействительно:
    • Выставив короля на проверку, переместив фигуру на ту же сторону, что и находящийся под угрозой исчезновения король. Это применимо только в том случае, если не противостоящая фигура делает ход, а не противостоящая фигура, движущаяся, чтобы поставить короля под контроль.
    • Оставить короля под контролем, и в этом случае он должен выйти из-под контроля. Поэтому, если король находится под контролем, и ход диктует, что другая фигура движется, это недопустимый ход, если только другая фигура не препятствует проверке. Часть может предотвратить проверку одним из двух способов: либо она берет часть, выполняющую проверку, либо затрудняет путь между частью, выполняющей проверку, и королем.
    • «Проверка» - это ситуация, в которой противник короля может (если это была их очередь двигаться) легально переместить фигуру на этого короля. Это правило не применяется рекурсивно, т. Е. Король находится под контролем, даже если ход противника на этого короля оставляет своего короля под контролем.
  • Пешки : пешка может двигаться вперед (то есть вверх, если белый, вниз, если черный) на один квадрат к незанятому. Есть также три особых ситуации:
    • Если пешка еще не сдвинулась (вы можете определить это с помощью Y-координаты; белые пешки не сместились, если их Y-координата равна 2, черные пешки не сместились, если их Y-координата равна 7), пешка разрешено перемещать два квадрата вперед на незанятый квадрат.
    • Если фигура противника находится по диагонали перед пешкой (то есть на квадрате к северо-западу или северо-востоку от пешки, если она белая, или к юго-западу или юго-востоку, если она черная), пешке разрешено двигаться на рассматриваемый квадрат.
    • Если пешка перемещается к конечной координате Y (8 для белых или 1 для черных) в нормальных шахматных правилах, она должна быть повышена до ферзя, ладьи, рыцаря или слона того же цвета. Для целей этого вопроса выбор продвижения не имеет значения, является ли ход действительным или нет (и не может быть выражен в формате ввода), но ходы пешки, которые приведут к продвижению, должны быть разрешены.
  • Епископы : Епископы могут перемещаться между 1 и 8 квадратами по любому непрерывному беспрепятственному межкардинальному (то есть диагональному) пути.
  • Рыцари : Рыцари могут двигаться вLформе, состоящей из следующих (эквивалентных) ходов:
    • Один квадрат в любом кардинальном направлении, затем поворот на 90/270 °, за которым следует последний шаг вперед на 2 клетки.
    • 2 квадрата в любом кардинальном направлении, затем поворот на 90/270 °, за которым следует последний шаг вперед на один квадрат.
    (Помните, что путь рыцаря не может быть заблокирован промежуточными фигурами, хотя его последний квадрат все еще должен быть законным.)
  • Грачи : Грачи могут перемещаться между 1 и 8 квадратами по любому непрерывному беспрепятственному кардинальному пути.
  • Королевы : Королевы могут перемещаться между 1 и 8 квадратами по любому непрерывному кардинальному или межкардинальному (то есть диагональному) беспрепятственному пути.
  • Короли : Короли движутся как королевы, за исключением того, что они ограничены перемещением только одного квадрата за ход (то есть король может перемещаться только в смежные по кардинальным или диагональным квадратам). Как напоминание, вы не можете сделать ход, который оставляет вашего короля в узде; таким образом, вы также не можете контролировать своего короля.

Правила шахмат также содержат специальные ходы, называемые «рокировка» и «en passant». Однако, поскольку законность этих ходов зависит от истории игры, а не только от текущей позиции (и потому что для рокировки требуется перемещение двух фигур одновременно, что не соответствует формату ввода), вы не должны рассматривать ни один из этих ходов. существовать (т. е. ход, который был бы рокировочным или пассивным, должен считаться незаконным).

Вы можете выводить любые два разных результата, чтобы указать правильность хода, и вы можете использовать вход любым способом. Вы также можете выбрать 0-индексацию вместо 1-индексации для позиций, если вы предпочитаете. Это , поэтому выигрывает самый короткий код!

Контрольные примеры

Board
Move => Output (Reason)

[["B", "K", 3, 8], ["B", "Q", 1, 5], ["B", "N", 4, 7], ["B", "N", 7, 8], ["B", "B", 2, 4], ["B", "R", 4, 8], ["B", "R", 8, 8], ["B", "P", 1, 7], ["B", "P", 2, 7], ["B", "P", 3, 6], ["B", "P", 5, 6], ["B", "P", 6, 7], ["B", "P", 7, 7], ["B", "P", 8, 7], ["W", "K", 5, 1], ["W", "Q", 6, 3], ["W", "N", 3, 3], ["W", "B", 5, 2], ["W", "B", 6, 4], ["W", "R", 1, 1], ["W", "R", 8, 1], ["W", "P", 1, 3], ["W", "P", 2, 2], ["W", "P", 3, 2], ["W", "P", 4, 4], ["W", "P", 6, 2], ["W", "P", 7, 2], ["W", "P", 8, 3]]
["W", "R", 8, 2] => True (The rook on h1 can move forward one)

[['B', 'K', 6, 8], ['B', 'Q', 1, 7], ['B', 'N', 1, 3], ['B', 'N', 7, 1], ['B', 'B', 8, 8], ['B', 'B', 2, 5], ['B', 'R', 4, 3], ['B', 'R', 1, 5], ['B', 'P', 5, 5], ['B', 'P', 7, 2], ['B', 'P', 5, 7], ['B', 'P', 5, 6], ['B', 'P', 4, 4], ['W', 'K', 7, 3], ['W', 'Q', 3, 2], ['W', 'N', 4, 8], ['W', 'N', 7, 5], ['W', 'B', 1, 1], ['W', 'B', 8, 1], ['W', 'R', 1, 8], ['W', 'R', 3, 7], ['W', 'P', 8, 2], ['W', 'P', 6, 3], ['W', 'P', 4, 2], ['W', 'P', 1, 4], ['W', 'P', 8, 7]]
['W', 'N', 1, 5] => False (Neither knight to move to a5 from where they are)

[['B', 'K', 7, 3], ['B', 'Q', 2, 4], ['B', 'N', 5, 2], ['B', 'N', 1, 6], ['B', 'B', 7, 7], ['B', 'B', 1, 8], ['W', 'K', 7, 1], ['W', 'Q', 6, 1], ['W', 'N', 5, 6], ['W', 'N', 3, 3], ['W', 'B', 2, 2], ['W', 'B', 6, 5]]
['B', 'K', 8, 3] => False (The white bishop would put the king in check)

[['B', 'K', 7, 6], ['B', 'Q', 8, 3], ['B', 'N', 7, 7], ['B', 'N', 8, 7], ['B', 'B', 2, 2], ['B', 'B', 3, 8], ['B', 'R', 1, 1], ['B', 'R', 1, 6], ['B', 'P', 8, 5], ['B', 'P', 4, 3], ['B', 'P', 8, 6], ['W', 'K', 7, 8], ['W', 'Q', 7, 2], ['W', 'N', 5, 1], ['W', 'N', 4, 6], ['W', 'B', 1, 2], ['W', 'B', 2, 6], ['W', 'R', 4, 4], ['W', 'R', 3, 6], ['W', 'P', 5, 2], ['W', 'P', 6, 2]]
['B', 'N', 5, 8] => False (The white queen currently has the king in check, and this move doesn't prevent that)

[['B', 'K', 7, 6], ['B', 'Q', 8, 3], ['B', 'N', 7, 7], ['B', 'N', 8, 7], ['B', 'B', 2, 2], ['B', 'B', 3, 8], ['B', 'R', 1, 1], ['B', 'R', 1, 6], ['B', 'P', 8, 5], ['B', 'P', 4, 3], ['B', 'P', 8, 6], ['W', 'K', 7, 8], ['W', 'Q', 7, 2], ['W', 'N', 5, 1], ['W', 'N', 4, 6], ['W', 'B', 1, 2], ['W', 'B', 2, 6], ['W', 'R', 4, 4], ['W', 'R', 3, 6], ['W', 'P', 5, 2], ['W', 'P', 6, 2]]
['B', 'N', 7, 5] => True (The king is in check, and the knight blocks that)

[['B', 'K', 8, 3], ['B', 'Q', 6, 5], ['B', 'N', 7, 8], ['B', 'N', 3, 7], ['B', 'B', 4, 1], ['B', 'B', 1, 1], ['W', 'K', 7, 7], ['W', 'Q', 7, 1], ['W', 'N', 2, 2], ['W', 'N', 1, 3], ['W', 'B', 3, 5]]
['B', 'B', 2, 2] => True (takes the white knight)

[['B', 'K', 6, 1], ['B', 'Q', 6, 2], ['W', 'K', 8, 1]]
['B', 'Q', 7, 1] => True (Smallest checkmate possible, in terms of bounding box)

Этот вызов был изолирован . Он получил отрицательные отзывы, без каких-либо объяснений, поэтому я решил опубликовать его в любом случае


«Часть на той же стороне движется, выставляя короля на проверку». - эта формулировка, кажется, не подходит сейчас, когда вы переместили заголовок, под которым она идет. Я бы изменил это на что-то вроде «Перемещение этого фрагмента подвергнет короля проверке»
FlipTack

Этот вопрос был отклонен в Песочнице, и теперь здесь без единого объяснения. Я ничего не могу сделать, чтобы заставить вас сказать мне, почему вы отказались от голосования, но, по крайней мере, имейте порядочность объяснить ваши действия, вместо того чтобы молчать в тени. Если вы думаете, что этот пост может быть улучшен, пожалуйста, предложите, как, а не делать выстрел, не объясняя себя.
caird coinheringaahing

2
Никто не отрицал это ...?
FlipTack

1
Можем ли мы получить 2d массив кусков в качестве входных данных?
овс

1
@ovs Да, это кажется приемлемым
caird coinheringaahing

Ответы:


3

Python 2python-шахматами ),  141 138 134 133  132 байта

Без какого-либо действительно интересного кода - но, может быть, это может конкурировать с языками игры в гольф или (смею упомянуть) Mathematica?

Примечание: питон-шахматы является PyPi пакет установки на Python 2.7.9+ с:
python -m pip install python-chess)

import chess
a,p,n=input()
S=chess.Board(a+' - - 0 1')
for m in S.legal_moves:1/(m.to_square!=n)**(`p`in`S.piece_at(m.from_square)`)

Полная программа, принимающая ввод из трех элементов:

  1. начало записи FEN - строка, содержащая первые два поля. Это делается для определения состояния платы И какого цвета она движется (поскольку это информация на входе в OP, тогда как поля с третьего по шестой «зафиксированы» OP, следовательно, не должны быть частью ввода)
  2. имя части, пытающейся переместиться (как дано в OP - один из PRNBQK)
  3. квадрат, на который именованная фигура пытается переместиться, где a1есть 0, b1есть 1, ... a2есть 8, ..., h8есть 63,

Программа выводит через свой код выхода с заданным допустимым вводом:

  • 1 если ход действительный (программа вызвала ошибку - из-за деления на ноль);
  • 0 это не так (программа вышла нормально)

(Не) Попробуйте онлайн! (потому что пакет python-chess не установлен там, а TIO не позволяет подключаться к Интернету, поэтому код pip-install в заголовке не будет работать).

Обратите внимание, что оператор питания в Python делает, 1**1 == 1**0 == 0**0 == 1но 0**1 == 0
..., следовательно, 1/0**1повышает ошибку деления на ноль в то время 1/1**1, как 1/1**0, и 1/0**0все успешно
(... и это в Python FalseиTrue равно 0и 1соответственно).


2
Это совершенно правильный ответ, но он немного похож на мошенничество, похожий на встроенный ответ Mathematica.
caird coinheringaahing

Да, отсюда и комментарий, который я поместил в верхней части «Без какого-либо действительно интересного кода ...», может быть, когда у меня будет немного времени, я сделаю Jelly (который не может импортировать этот модуль :))
Джонатан Аллан

1
... учти, это все же потребовало некоторых усилий.
Джонатан Аллан

Перестановка str(S.piece_at(m.from_square))==p forна p==str(S.piece_at(m.from_square))for, что следует сохранить один байт.
Захари

Ах, да - спасибо @ Zacharý Я просто искал, смогу ли я разобрать с reprпомощью обратных галочек, чтобы заменить, strчтобы сохранить ...
Джонатан Аллан

3

Regex (PCRE2), 931 925 837 байт

Это решение отличается от постановки задачи тем, что в регулярное выражение передаются два состояния доски, а не одно состояние доски и ход. Ход сделан из разницы между двумя состояниями доски. Таким образом, я выполнил задачу программы TIO, чтобы взять тестовые случаи в формате, предоставленном этим вопросом, найти все экземпляры описанного фрагмента на доске и вместе с каждым попытаться переместить его в конечную позицию и оценить регулярное выражение с этой возможностью, если регулярное выражение обнаружит, что оно есть, то оно действительно. Если это не хорошо, дайте мне знать; Можно реализовать регулярное выражение как позиция + движение, но это будет гораздо менее элегантно и потребует серьезного рефакторинга.

Доска представлена в 8 × 8 ASCII , где белые фигуры являются заглавными буквами и черного в нижнем регистре: Р ости, к N РАВО, В ishop, R ООК, Q ueen, К Ing. Сторона черных (8-й ранг) находится сверху, а сторона белых (1-й ранг) - снизу. Каждый ранг отделяется новой строкой, а пустые квадраты помечаются как -. Две позиции на доске разделены дополнительной новой строкой.

Настоящая цель этого проекта - проверить целые игры, а не только отдельные движения. Смотрите ниже текущее состояние дел.

()?(?>|((.|
(?=.)){2})((?=(\X{72})-))((?=(?(1)[-a-z]|[-A-Z])))((?5)(?(?=(.*
)
)[qnrb]|p))((?5)(?(?=(?8){8}
)[QNRB]|P)))(?>((.)(?=(?5)\11)|(?(m)$)((?(1)(-(?=(?9))(?=(?3){8}((?3){9})?P(?4))(?(-1)(?=(?8){4}
))|[a-z](?=(?9))(?=(?3){7}(?2)?P(?4)))|(p(?4)((?=(?3){8}((?3){9})?-(?7))(?(-1)(?=(?8){7}
))|(?=(?3){7}(?2)?[A-Z](?7)))))|(?<e>(?6).)?(?=(?i:(?|(?(e)|(B|Q))(?27)(?(e)(B|Q))|(?(e)|(R|Q))(?31)(?(e)(R|Q))|(?(e)|(N))(?34)(?(e)(N))|(?(e)|(K))(?35)?(?(e)(K))))(?(e)(?<=(?!(?6)).)(?4)|(?6).(?5)\19))(?(e)(?=(?5)\20)|(?!(?6)).(?4)))(?<m>)|(?(+1)$)(.))+
)+\k<m>
(?!\X{0,70}((?(1)p|k)(?=(?3){7}(?2)?(?(1)K|P))|(?i:(?<E>(?!(?6))K)?((?(E)|((?6)[BQ]))(()?((?(-1)-)(?3){7}(?(-2)(?2)))+)(?(E)(?-4))|(?(E)|((?6)[RQ]))(-*|((?(-1)-)(?3){8})+)(?(E)(?-3))|(?(E)|((?6)N))((?<=..)(?2){3}|(?=.)(?2){5}|(?2){8}(?2)?)(?(E)(?-2)))(?(E)|(?&E))|K((?3){7,9})?K)))

Попробуйте онлайн!

Довольно напечатанный, и частично не выгравированный (абсолютные обратные ссылки изменены на относительные, а группы захвата изменены на не захватывающие, или в некоторых случаях атомарные для скорости):

# Chess move validation regex (PCRE)
()?                 # decide whether to evaluate this as white's or black's move; \1 set = white, \1 unset (NPCG) = black
(?>|                # subroutines:
  ((.|\n(?=.)){2})                  # (?3) = for moving within the board, without wrapping to the next board, (?2) = (?3){2}
  ((?=                              # (?4) = assert that position of just-consumed piece is vacated on the next turn
    (\X{72})                        # (?5) = skip to the position of the just-consumed piece on the next turn
  -))
  ((?=(?(1)[-a-z]|[-A-Z])))         # (?6) = assert that the piece at the current position belongs to the current player's opponent or is empty
  ((?5)(?(?=(.*\n)\n)[qnrb]|p))     # (?7) = black pawn that might be promoted, (?8) = .*\n
  ((?5)(?(?=(?8){8}\n)[QNRB]|P))    # (?9) = white pawn that might be promoted
)
(?>
  (?>
    # Handle squares that don't change (empty->empty or pieces that doesn't move)
    (.)(?=(?5)\g{-1}) |
    # Handle a piece that moves (and optionally captures an enemy piece)
    (?(m)$)  # allow only one move to be made per turn
    (?>
      (?(1)
        (?:                                                         # white pawn
            -  (?=(?9))(?=(?3){8}((?3){9})?P(?4))(?(-1)(?=(?8){4}\n)) |   # move 1 or 2 spaces forward
          [a-z](?=(?9))(?=(?3){7}(?2)?     P(?4))                     )   # capture diagonally
      |
        (?:p(?4)(?:                                                 # black pawn
          (?=(?3){8}((?3){9})?  -  (?7))(?(-1)(?=(?8){7}\n)) |            # move 1 or 2 spaces forward
          (?=(?3){7}(?2)?     [A-Z](?7)) )                   )            # capture diagonally
      ) |
      # bishops, rooks, queens, knights, or kings
      (?<e>(?6).)?   # decide between scanning forward (<e> is unset) or backwards (<e> is captured)
      (?=
        (?i:
          (?|
            (?(e)|(B|Q)) (?&B)  (?(e)(B|Q)) | # bishops or queens
            (?(e)|(R|Q)) (?&R)  (?(e)(R|Q)) | # rooks or queens
            (?(e)|(N  )) (?&N)  (?(e)(N  )) | # knights
            (?(e)|(K  )) (?&K)? (?(e)(K  ))   # kings
          )
        )
        (?(e)(?<=(?!(?6)).)(?4)|(?6).(?5)\g{-2})   # verify that the piece moved, and optionally captured piece, are of the correct color
      )
      (?(e)(?=(?5)\g{-1})|(?!(?6)).(?4))   # verify that the piece moved is the same type and color at its destination in the next turn's board position
    )(?<m>) |
    (?(+1)$)(.)  # handle the destination/source square that a piece moved to/from (only allow matching one of these per turn)
  )+\n
)+
\k<m>         # assert that a move has taken place
\n
# don't allow moving into check  
(?!
  \X{0,70}
  (?:
    # pawns (capture diagonally)
    (?(1)p|k)(?=(?3){7}(?2)?(?(1)K|P)) |
    # bishops, rooks, queens, knights, or kings
    (?i:
      (?<E>(?!(?6))K)?   # decide between scanning forward (<E> is unset) or backwards (<E> is captured)
      (?:
        (?(E)|((?6)[BQ])) (?<B>()?((?(-1)-)(?3){7}(?(-2)(?2)))+)         (?(E)(?-4)) | # bishops or queens
        (?(E)|((?6)[RQ])) (?<R>-*|((?(-1)-)(?3){8})+)                    (?(E)(?-3)) | # rooks or queens
        (?(E)|((?6) N  )) (?<N>(?<=..)(?2){3}|(?=.)(?2){5}|(?2){8}(?2)?) (?(E)(?-2))   # knights
      )
      (?(E)|(?&E)) |
      K(?<K>(?3){7,9})?K   # kings
    )
  )
)

-88 байт с помощью неатомарных вызовов подпрограмм, таким образом, переназначение с PCRE1 на PCRE2

Вышеприведенная версия была изменена, чтобы не допустить прохода или рокировки, но полный проект в настоящее время находится в состоянии, в котором он проверяет каждый тип хода, начиная с начального состояния доски (которое должно быть стандартной начальной позицией шахмат - Chess960 не является поддерживается, пока как минимум). Полные правила en passant и рокировки применяются.

Вот пример игры, подтвержденной полным регулярным выражением (PCRE1 - еще не ретаргетинг) [regex101.com] .

Неверный ход приведет к тому, что каждая последующая позиция на доске не будет совпадать / выделяться. Определение матов / патов и, следовательно, определение того, кто является победителем (или, если это ничья), еще не реализовано; вот почему окончательное состояние платы в этом примере не выделено.

Вот программа на C / C ++, которая преобразует алгебраическую нотацию в формат, распознаваемый этим регулярным выражением. Алгебраическая нотация в настоящее время должна быть представлена ​​в виде массива, встроенного в исходный код, с отдельными строками для каждого хода, но считывая его как одну строку из stdin или аргумента командной строки, с полной последовательностью ходов, разделенных пробелами. и числа хода с точками в конце, планируется.

Я также начал с регулярного выражения, которое проверяет полную игру исключительно в алгебраической шахматной нотации с подразумеваемой стандартной начальной позицией. Все, что ему нужно, это пустая «доска для заметок», добавленная в конце ввода (после списка ходов). Я почти уверен, что можно реализовать это полностью и планировать когда-нибудь закончить.


Я не был напуган этим большим страхом с тех пор, как я закашлял 3000-байтовое чудовище регулярных выражений для вопроса проверки судоку (серьезная ошибка, учитывая, что победный ответ получил его менее чем за 75). Действительно доказывает тот факт, что иногда, когда вы используете регулярные выражения для решения проблемы, вы сталкиваетесь с двумя проблемами
Value Ink

@ValueInk Хех, может быть, вы правы, но мне это нравится независимо от (или, может быть, из-за) его полной непрактичности. Ваш комментарий вдохновил меня попытаться ответить на этот вопрос судоку, но я обработал только 200 байтов . Ну что ж.
Deadcode
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.