Automatische Preisidentifikation von Schriftrollen in Nethack


21

OH GÖTTER NEIN !! Du kannst uns nicht mit Doorknob hier lassen! Es wird überall Nethack sein! - vor 1 Tg. Von Geobits

Kann mich nicht enttäuschen ...

Einführung

(Sie können diesen Abschnitt überspringen, wenn Sie sich nicht für Exposition interessieren und / oder wenn Sie Tab Explosion Syndrom haben )

Eine der charakteristischen Mechanismen von Nethack (und Rogue und ähnlichen Spielen im gleichen Roguelike-Genre) ist das Identifikationssystem . Zu Beginn des Spiels werden nur Gegenstände in Ihrem Anfangsinventar "formal identifiziert". Die überwiegende Mehrheit anderer Objekte beginnt unbekannt; Beispielsweise wird ein "Reflexionsschild" zunächst als "polierter Silberschild" angezeigt, bevor er identifiziert wird.

Ein "polierter Silberschild" kann nur ein Reflexionsschild sein , was aber in zwei anderen Fällen interessante Konsequenzen hat.

  1. Einige Elemente unterscheiden sich voneinander, haben jedoch das gleiche "Erscheinungsbild". Wenn Sie beispielsweise einen " grauen Stein " finden, kann dies eines von vier Dingen sein: Ein Feuerstein (nutzlos), ein Prüfstein (kann nützlich sein), ein Ladestein (der Sie schwer belastet, weil er eine Tonne wiegt und Sie Ich kann es nicht fallen lassen) oder einen Glücksstein (äußerst hilfreich, fast notwendig, um das Spiel zu gewinnen).

  2. Viele Gegenstände (Schriftrollen, Zauberstäbe, Ringe, Zauberbücher, einige Rüstungen usw.) haben ein zufälliges Aussehen. Dies bedeutet, dass es eine festgelegte Liste möglicher Erscheinungen gibt, die Tränke haben könnten. zum Beispiel [ goldener Trank , wirbelnder Trank , kohlensäurehaltiger Trank , purpurroter Trank usw.]. Diese Erscheinungen werden dann nach dem Zufallsprinzip dem zugeteilt, was sie tatsächlich sind ( Heiltrank , Trank der Lähmung , Trank des Unsichtbaren , Trank des Polymorphen usw.).

    Das bedeutet , dass ein sechseckiger Amulett könnte Ihr Leben in einem Spiel (Amulett von lebensrettend) speichern , und ersticken Sie zum Tod des nächsten (Amulett der Strangulation) .

Das macht das Identifizieren von Gegenständen natürlich zu einem wichtigen Teil des Spiels. Gegenstände können "formal identifiziert" werden, was bedeutet, dass sie eindeutig als ein bestimmter Gegenstand angezeigt werden (z. B. werden alle Juwelenstäbe, die Sie finden, als Zauberstäbe von Monster erschaffen angezeigt ); Dies geschieht in erster Linie über Schriftrollen oder Zauberbücher . In der Regel sind diese jedoch Mangelware, was uns zu ...

Informelle Identifizierung. Dies bedeutet, dass Sie ziemlich sicher (oder sicher) sind, dass ein bestimmtes nicht identifiziertes Element von einem bestimmten Typ ist (oder dass es nur einer von mehreren Typen sein kann), aber Sie haben es noch nicht "formal" identifiziert. Dies kann über verschiedene Methoden erfolgen: Gravurprüfung auf Stäbe, Senkprüfung auf Ringe oder die am häufigsten verwendete Methode ...

Scroll-Preis-ID-Tabelle

... Preisausweis ! Worum geht es bei dieser Herausforderung?

Kurz gesagt, es gibt Geschäfte in den Dungeons of Doom (ja, die Ladenbesitzer hielten es für eine gute Idee, ein Geschäft in einem unterirdischen Verlies einzurichten; fragen Sie nicht warum). In diesen Geschäften können Sie die verschiedenen Gegenstände kaufen und verkaufen, die Sie auf Ihren Reisen antreffen. Beim Kauf oder Verkauf eines Artikels teilt Ihnen der Ladenbesitzer zunächst mit, wie viel er Ihnen verkaufen / von Ihnen kaufen würde. Da für bestimmte Artikel bestimmte Preise garantiert sind , können Sie diese verwenden, um einen bestimmten Artikeltyp informell zu identifizieren .

Einige Gegenstände, wie die Lichtrolle , sind die einzigen Gegenstände, die einen bestimmten Betrag kosten, wodurch Sie sie eindeutig identifizieren können. Die meisten Artikel teilen sich jedoch eine Preisgruppe mit anderen Artikeln derselben Klasse, sodass Sie nur die Möglichkeiten einschränken können (was immer noch nützlich ist). Die Kauf- und Verkaufspreise eines Artikels werden jedoch von einer Reihe von Variablen beeinflusst (wie z. B. Ihrer Charismastatistik ). Daher die obige Grafik.

Kannst du mir sagen, dass ich Nethack mag?

Eingang

Die Eingabe erfolgt als (Vanille, 3.4.3) Nethack-Spiel, das gerade gespielt wird:

"For you, most gracious sir; only 177 for this scroll labeled VERR YED HORRE."
--More--

        ------------
        |          .                                        ---------
        |          |                         ----------     |    ^  |
        |          .##       ################.        +#   #.       .#
        |          | #                       |       _|#   #---------#
        |          | ###                   ##.<       |#   ####      #
        |          .#########################----------#      #      #
        ------------   ###     #         ############# #      #      #
           #             #  -----------  #             #      #   ####
         ###             ###|         |###             #      #   #----------
         #                 #.         |#             ### #    #   #|.???????|
        ##                  |         |#             #--------#   #|.??@????|
    ----.----###############.         |#             #|      |#   #-@???????|
    |.......+#              |         |#             #.      |#    ----------
    |.......|               |         .#              |      |#
    |......>|               -----------               |      +#
    ---------                                         --------


Wizard the Evoker         St:12 Dx:14 Co:11 In:16 Wi:12 Ch:10  Chaotic
Dlvl:2  $:0  HP:11(11) Pw:0(8) AC:9  Exp:1 T:11

Dies bedeutet, dass mehrere Eigenschaften garantiert sind:

  • Es wird immer 24 Zeilen lang sein.

  • Jede Zeile hat immer eine Länge von maximal 80 Zeichen.

  • Die vorletzte Zeile besteht aus den folgenden " Token ": Name und Titel des Spielers (in Form von " foo the bar "), Liste der Attribute (durch ein einzelnes Leerzeichen getrennt) und Ausrichtung des Spielers ( Erlaubt, neutral oder chaotisch). Jedes Token wird durch eine variable Anzahl von Leerzeichen getrennt. 1

  • Die Liste der Attribute wird immer so sein St:* Dx:* Co:* In:* Wi:* Ch:*, dass ein *Zeichen eine ganze Zahl von 3 bis 25 darstellt. 2 (Der Punkt von Interesse hier ist der letzte Wert, Charisma, den Sie zur Berechnung der Preise benötigen.)

  • Die erste Zeile besteht immer aus einer geschäftsbezogenen Nachricht (insbesondere der Nachricht, die angezeigt wird, wenn Sie einen Artikel kaufen oder verkaufen). Darüber hinaus wird garantiert, dass es sich bei diesem Artikel um eine einzelne, nicht identifizierte, nicht benannte Schriftrolle handelt. Für den Kauf eines Artikels ist dies:

    "For you, {TITLE}; only {PRICE} for this scroll labeled {LABEL}."--More--
    

    und für den Verkauf ist es:

    {SHK} offers {PRICE} gold pieces for your scroll labeled {LABEL}.  Sell it? [ynaq] (y)
    

    Hier sind die "Variablen" aufgeführt {curly braces}:

    • {TITLE}ist immer eine der " guten ", " geehrten ", " gnädigsten " oder " geschätzten ", entweder mit " Frau " oder " Herr " verkettet .

    • {PRICE} ist immer eine ganze Zahl.

    • {LABEL}wird immer eine der folgenden sein ( Quelle ):

      ZELGO MER       JUYED AWK YACC  NR 9             XIXAXA XOXAXA XUXAXA
      PRATYAVAYAH     DAIYEN FOOELS   LEP GEX VEN ZEA  PRIRUTSENIE
      ELBIB YLOH      VERR YED HORRE  VENZAR BORGAVVE  THARR
      YUM YUM         KERNOD WEL      ELAM EBOW        DUAM XNAHT
      ANDOVA BEGARIN  KIRJE           VE FORBRYDERNE   HACKEM MUCHE
      VELOX NEB       FOOBIE BLETCH   TEMOV            GARVEN DEH
      READ ME
      
    • {SHK}wird immer eine der folgenden sein ( Quelle ):

      Skibbereen      Ballingeary     Inishbofin      Annootok        Abitibi
      Kanturk         Kilgarvan       Kesh            Upernavik       Maganasipi
      Rath Luirc      Cahersiveen     Hebiwerie       Angmagssalik    Akureyri
      Ennistymon      Glenbeigh       Possogroenoe    Aklavik         Kopasker
      Lahinch         Kilmihil        Asidonhopo      Inuvik          Budereyri
      Kinnegad        Kiltamagh       Manlobbi        Tuktoyaktuk     Akranes
      Lugnaquillia    Droichead Atha  Adjama          Chicoutimi      Bordeyri
      Enniscorthy     Inniscrone      Pakka Pakka     Ouiatchouane    Holmavik
      Gweebarra       Clonegal        Kabalebo        Chibougamau     Lucrezia
      Kittamagh       Lisnaskea       Wonotobo        Matagami        Dirk
      Nenagh          Culdaff         Akalapi         Kipawa
      Sneem           Dunfanaghy      Sipaliwini      Kinojevis
      

    Diese Nachricht kann in eine andere Zeile aufgeteilt werden (nimmt jedoch nie mehr als 2 Zeilen ein). 3

  • Abgesehen von den ersten paar Zeilen sind alle Wetten deaktiviert, wie der Rest des Bildschirms aussieht. Nethack verwendet den größten Teil des ASCII-Zeichensatzes . Das einzige, was Sie mit Sicherheit annehmen können, ist, dass die Eingabe rein ASCII sein wird (dies spielt jedoch wahrscheinlich keine Rolle, da Sie die Zeilen 3-22 sowieso verwerfen können).

Wenn die Eingabe als Funktionsargument verwendet wird, wird sie genau wie im obigen Beispiel angegeben (durch Zeilenumbrüche getrennt). Wenn Sie über STDIN eingeben, werden 24 aufeinanderfolgende Eingabezeilen angezeigt (wie oben gezeigt). Sie können wählen, ob die Eingabe eine nachgestellte Zeile haben soll oder nicht. Die Eingabe darf garantiert keine nachgestellten Leerzeichen enthalten.

Ausgabe

Die Ausgabe sollte so erfolgen, wie ich #namedie Schriftrolle sollte, die ich gerade preisidentifiziert habe. Das von mir verwendete Namenssystem (und das ich bei anderen gesehen habe) ist:

  • Wenn die Schriftrolle eindeutig als eine bestimmte Schriftrolle identifiziert wird (identifiziere, leichte, verzauberte Waffe), ist #namees das. Dies ist der Fall für Schriftrollen mit den folgenden Grundpreisen (die Berechnung des Grundpreises finden Sie weiter unten): 20 -> Identifizierung, 50 -> Licht, 60 -> Waffe verzaubern.

  • Nehmen Sie andernfalls die ersten drei Buchstaben des Erscheinungsbilds der Schriftrolle oder das erste Wort, wenn es weniger als 3 Zeichen enthält. Zum Beispiel ZELGO MERwird ZEL, VE FORBRYDERNEwird VE, etc. Verketten Sie mit diesem (einem Leerzeichen und dann) den Basispreis der Schriftrolle. Zum Beispiel ELB 300.

  • Wenn der Grundpreis eine von zwei Möglichkeiten sein kann, versuche ich normalerweise, den Artikel zu kaufen oder zu verkaufen, bis ich einen Angebotspreis erhalte, der ihn eindeutig in einen bestimmten Preisschlitz einordnet. Dies ist bei dieser Herausforderung jedoch nicht möglich. Trennen Sie die beiden möglichen Basispreise mit einem Schrägstrich ( /). Zum Beispiel HAC 60/80.

Hier ist die Formel zum Umrechnen des Grundpreises eines Artikels in den Preis, den Sie für den Kauf erhalten:

  • Beginnen Sie mit dem Grundpreis des Artikels

  • Chance auf einen etwaigen 33% igen "nicht identifizierten Zuschlag", berechnet über price += price / 3

  • eine weitere Chance von 33% "Sucker Markup" (dies ist eigentlich keine zufällige Chance, aber für die Zwecke dieser Herausforderung ist es), berechnet auf die gleiche Weise

  • ein Charisma-Modifikator, der wie folgt angewendet wird:

    Ch    3-5     6-7       8-10      11-15  16-17     18        19-25
    Mod   +100%   +50%      +33%      +0%    -25%      -33%      -50%
    Code  p *= 2  p += p/2  p += p/3  ---    p -= p/4  p -= p/3  p /= 2
    

Und hier ist die Formel für den Grundpreis -> Verkaufspreis:

  • Beginnen Sie mit dem Grundpreis des Artikels

  • dividiere dies durch 2 oder 3 ("normal" oder "sucker markup")

  • Chance auf weitere 25% 4 , berechnet überprice -= price / 4

Die Division ist eine Ganzzahldivision, was bedeutet, dass das Ergebnis bei jedem Schritt abgerundet wird. (Quelle: Wiki , und ein bisschen Quellcode ausgraben. Das Umkehren dieser Formeln ist Ihre Aufgabe.)

Zum Schluss ein handliches ASCII-Diagramm, das die möglichen Kaufpreise (gruppiert nach Charisma-Statistik) und Verkaufspreise einer Schriftrolle mit einem bestimmten Grundpreis zeigt:

Base  Ch<6          6-7          8-10         11-15        16-17        18           19-25        Sell
20    40/52/68      30/39/51     26/34/45     20/26/34     15/20/26     14/18/23     10/13/17     5/6/8/10
50    100/132/176   75/99/132    66/88/117    50/66/88     38/50/66     34/44/59     25/33/44     12/16/19/25
60    120/160/212   90/120/159   80/106/141   60/80/106    45/60/80     40/54/71     30/40/53     15/20/23/30
80    160/212/282   120/159/211  106/141/188  80/106/141   60/80/106    54/71/94     40/53/70     20/26/30/40
100   200/266/354   150/199/265  133/177/236  100/133/177  75/100/133   67/89/118    50/66/88     25/33/38/50
200   400/532/708   300/399/531  266/354/472  200/266/354  150/200/266  134/178/236  100/133/177  50/66/75/100
300   600/800/1066  450/600/799  400/533/710  300/400/533  225/300/400  200/267/356  150/200/266  75/100/113/150

(Dies ist identisch mit dem Diagramm im Wiki, mit der Ausnahme, dass alle möglichen Verkaufspreise aufgelistet werden, während im Wiki-Diagramm nicht zwei der vier möglichen Verkaufspreise angegeben sind. Nein, ich habe dieses Diagramm nicht manuell erstellt. Es wurde mit diesem Ruby-Skript erstellt .)

Testfälle

Eingang:

"For you, honored sir; only 80 for this scroll labeled LEP GEX VEN ZEA."
--More--                   #                                          #
                      ----------------                              -----
                      |              |              ------------####+   |
      -----           |              -##############+          .#   |   |
      |   .###########|           >  |#           # |          |  ##.   |
      |   |          #------------.---#           ##.          |  # -----
      -+---          ##################             ----.-------###    #
       ####                     ###                     #       #      #
          #                     #                       #     ###      ###
          ###                 ###                       #     #          #
            #                 #                         #   ###     -----|--
       -----.---            ###                     ----+---#       |...@..|
       |       |            #                       |      |#       |???+??|
       |  <    .#          ##                     ##+      |        |+?????|
       |       |#    ------.-------                 |      |        |??]?@?|
       ---------###  |            |                 |      |        --------
            #     #  |            |                 --------
                  ###|            |                       #
                    #+            |
                     --------------

Wizard the Evoker         St:11 Dx:15 Co:9 In:20 Wi:9 Ch:11  Chaotic
Dlvl:7  $:0  HP:11(11) Pw:1(8) AC:9  Exp:1

Ausgabe: LEP 60/80


Eingang:

"For you, most gracious sir; only 80 for this scroll labeled DAIYEN FOOELS."
--More--                                                             #
                ------------                         -----      -------
 -----          |          |                         |   |      |     |
 |!)%|          |          |     ---------------     |   |     #-     |
 |*[@|          |          .#####|   <         |#####.   |   ###|     |
 |?(?|          ---------.--    #+             |#    |   |   #  |     |
 |[!(|                  ##       |             |#    |   +#### #.     .#
 |.@.|         ##################.             +#    ---.-     #|     |#
 ---|-                ###        ---------------#       ##     #-------#
    ##                #                    ######        #     #       #
     #              ###                         #        #     #       #
     ##             #                           #        #     #       #
  ------        #####                           #        #     #       #
  |    |       -.----                           #        #     #       #
  |    .#####  |^   |                        ####        #     #       #
  |    |    #  |    |          ----          #-----------.---- #       #------
  |    |    ###|    |          |  |          #.      >       | #       #|    |
  ------      #.    |          |  |           |              .##       #|    |
               |    |          ----           |              |         #.    |
               ------                         ----------------          ------

Wizard the Evoker         St:11 Dx:14 Co:16 In:15 Wi:10 Ch:9  Chaotic
Dlvl:6  $:0  HP:11(11) Pw:9(9) AC:9  Exp:1

Ausgabe: enchant weapon


Eingang:

Aklavik offers 113 gold pieces for your scroll labeled GARVEN DEH.  Sell it?
[ynaq] (y)

     -----      ------                                 ---------      -------
     |   |      |    |                         #     ##.       |      |.?)%/|
     |   |    ##.    |                       -----   # |       |      |.@!=*|
     |<  |    # |    |            #        ##.   .#####+    >  |#    #-.*?@[|
     |   .##### |    |      ------------   # | { |#    |^      |#    #|.=%)+|
     ---.-      |    |      |          .#### |   |#    ---------##   #-------
       ##       -.----     #.          |     |   |#          # ###   #
       #         ########  #|          .##   |   |#            ##    #
     ###                #  #------------ #   -----#          ####    #
     #                  #######          ##########################  #
     #                    #   #                            ###----.--#
     #                    ### #                            # #|     |#
   --.----       ########################################### #.     |#
   |     |       #----------.-#                               |     |#
   |     |       #|          |#                               -------
   |     |       #|          .#
   |     |########|          |
   -------        ------------
                   #    #
Wizard the Evoker         St:9 Dx:14 Co:11 In:19 Wi:10 Ch:12  Chaotic
Dlvl:4  $:0  HP:11(11) Pw:5(9) AC:9  Exp:1 Satiated

Ausgabe: GAR 300


Eingang:

"For you, good lady; only 67 for this scroll labeled VE FORBRYDERNE."--More--

                                                 -------
                                               ##|     |
  ------------                                 # |     |
  |+[!/!?%[?)|                               ### |     |          --------
  |[)(!/+]?!@|               #               #   |     |        ##+      |
  |.......@..|            --------------   ###   | <   | ##       |      |
  --------+---           #|            |   #     |     |  #       |    > |
          #            ###|            .####     --.----  ###    #-      |
          #            ###.            |           #        #  ###|      |
          #          #### ---.----------           #        ######.      |
          #          ####    ##                    #         ###  --------
          #        ####       #                    #         #
          #        ####       ########################     ###
        ###      ####                            ----+---- #
        #   #    ####                            |       .##
    ----.------####                              |  ^    |
    |         +####                              |    >  |
    |         |                                  | ^     |
    -----------                                  ---------

Wizard the Evoker         St:18 Dx:18 Co:16 In:20 Wi:20 Ch:18  Chaotic
Dlvl:4  $:150 HP:11(11) Pw:5(7) AC:9  Exp:1

Ausgabe: VE 100


Eingang:

Droichead Atha offers 5 gold pieces for your scroll labeled XIXAXA XOXAXA
XUXAXA.  Sell it? [ynaq] (y)
                                                ------------
                   -----                        |          .#
                   |   .### -----------        #.       {  |#
       -----       |   |  # |         |      ###|          |#
       |   .#     #.   |  # |         |      #  ---------+--#
       |   |    ###-|---    |         .##  ###          ##  #
       |   |    #   # #     |         | #  #            #   #
       |   -#####   #       |         | #### ############   #
       |>  | ##     #       ---------+-  ## -.----------    # ----------
       |   .####    ###             ## #####|          |    # |.*??/?)*|
       -----   #      #             #  #    |          |    # |@*)%!)]%|
               ###    ###         ######    |          |    # |.=)!%*!!|
                 #      #         #  #      |          |    ##+@*[%)(%?|
                 #####################      |          |      |.]?*?)%%|
                    -----+---.----##########.          |      |.%)%!!!%|
                    |            +##        ------------      ----------
                    |    <       |                  #
                    |            |
                    --------------

Wizard the Digger          St:11 Dx:9 Co:14 In:6 Wi:6 Ch:9  Lawful
Dlvl:3  $:0  HP:15(15) Pw:0(1) AC:9  Exp:1

Ausgabe: identify

(Ich musste Nethack manuell kompilieren, wobei alle anderen Namen der Ladenbesitzer entfernt wurden, da ich keinen Ladenbesitzer fand, der ein Leerzeichen in seinem Namen hatte ...)

Regeln


1: Dies ist nicht unbedingt immer wahr während eines Nethack-Spiels, aber wir nehmen dies der Einfachheit halber an.

2: wieder nicht immer wahr. Die Stärke kann zwischen 18/01 und 18 / ** liegen, aber Sie müssen damit nicht umgehen.

3: weitere grobe Vereinfachungen. Zum Beispiel kann ein Ladenbesitzer Sie als "Abschaum" oder "berühmteste und heiligste Kreatur" bezeichnen, aber Sie müssen damit nichts anfangen.

4: was ein schlauer Spieler umgeht, indem er wiederholt anbietet, den Gegenstand zu verkaufen, bis der höhere Preis gegeben ist.


15
Heilige Textwand!
Orlp

Antworten:


10

Javascript (ES6), 1610 724 601 612 419 405 Bytes

a=>(b=a.match(/(\d+) (g|f).+d (\w{0,3})[\s\S]+h:(\d+)/m),c=+b[4],d=[20,50,60,80,100,200,300].map(e=>(f=e>>1,g=~~(e/3),[e,b[2]=='g'?[g-(g>>2),g,f-(f>>2),f]:[e,e+g,e+g+~~((e+g)/3)].map(h=>c<6?h*2:c<8?h+h>>1:c<11?h+~~(h/3):c<16?h:c<18?h-(h>>2):c<19?h-~~(h/3):h>>1)])).filter(i=>i[1].includes(+b[1])),j={20:'identify',50:'light',60:'enchant weapon'}[d[0][0]],k=b[3]+' '+d[0][0],d.length==1?j||k:k+'/'+d[1][0])

Große Textwand, große Codewand treffen.

Ungolfed

inp => (
    extraction = inp.match(/(\d+) (g|f).+d (\w{0,3})[\s\S]+h:(\d+)/m),

    charisma = +extraction[4],

    allowed = [20, 50, 60, 80, 100, 200, 300].map(base => (
        tmp1 = base >> 1,
        tmp2 = ~~(base / 3),
        [
            base,
            extraction[2] == 'g' ?
                [tmp2 - (tmp2 >> 2), tmp2, tmp1 - (tmp1 >> 2), tmp1]
            :
                [base, base + tmp2, base + tmp2 + ~~((base + tmp2) / 3)].map(val =>
                    charisma < 6 ?
                        val * 2
                    : charisma < 8 ?
                        val + val >> 1
                    : charisma < 11 ?
                        val + ~~(val / 3)
                    : charisma < 16 ?
                        val
                    : charisma < 18 ?
                        val - (val >> 2)
                    : charisma < 19 ?
                        val - ~~(val / 3)
                    : val >> 1
            )
        ]
    )).filter(key => key[1].includes(+extraction[1])),

    name_ = {
        20: 'identify',
        50: 'light',
        60: 'enchant weapon'
    }[allowed[0][0]],

    tmp3 = extraction[3] + ' ' + allowed[0][0],

    allowed.length == 1 ?
        name_ || tmp3
    :
        tmp3 + '/' + allowed[1][0]
)

Beispiel


1
Das ist ein großer Stapel von Drehbüchern.
Fatalize 30.11.15

1
Woah, hat das endlich jemand beantwortet? Nizza: D
Türklinke

@Doorknob Wollte dies für eine Weile aktualisieren, kam endlich dazu.
Usandfriends
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.