Plagiator: Unterschied zwischen den Versionen
(Die Seite wurde neu angelegt: „''Plagiator'' ist ein von {{Benutzer|ultimate}} geschriebener Kartengenerator. Seit dem <tbd> ist er in Karopapier eingebunden. Der Generator ist kein "normal…“) |
|||
(4 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
Zeile 29: | Zeile 29: | ||
== Skalierungsalgorithmen == | == Skalierungsalgorithmen == | ||
− | Die Herausforderung beim Skalieren von Karten besteht darin sicherzustellen, dass das Ergebnis einerseits optisch ansprechend, aber trotzdem auch spannend zu fahren ist. Dazu habe ich ({{Benutzer|ultimate}}) ein bisschen rumprobiert und zwei unterschiedliche Algorithmen implementiert, die man enstprechend auswählen kann | + | Die Herausforderung beim Skalieren von Karten besteht darin sicherzustellen, dass das Ergebnis einerseits optisch ansprechend, aber trotzdem auch spannend zu fahren ist. Dazu habe ich ({{Benutzer|ultimate}}) ein bisschen rumprobiert und zwei unterschiedliche Algorithmen implementiert, die man enstprechend auswählen kann. |
+ | |||
+ | (Beispiele siehe [[#Beispielkarten]]) | ||
=== nearestneighbor === | === nearestneighbor === | ||
+ | |||
+ | Der Nearest-Neighbor-Algorithmus ist ein aus der Bildverarbeitung bekannter Algorithmus. Die Zielkoordinaten werden einfach mit dem Skalierungsfaktor verrechnet und vom Ergebnis Nachkommastellen abgeschnitten um auf die Ursprungskoordinaten zu kommen. Bei Faktor 2 wird also beispielsweise jedes Karo durch 2x2 identische Karos ersetzt... | ||
=== ultimate === | === ultimate === | ||
+ | |||
+ | Da bei Verwendung des Nearest-Neighbor-Algorithmus zwangsläufig "langweilige" Kartenelemente entstehen können - wie z.B. einfach nur größere Versionen von schachbrettartigen Stellen, wollte ich einen Algorithmus bauen, der für Karopapier optimierte Ergebnisse ausgibt, die spannender sind. Dazu wird bei der Skalierung zwischen Straßenfeldern (also Asphalt, CPs, Start, Ziel) und nicht-Straßenfeldern (Gras, Sand, Wasser, etc.) unterschieden und unter der Berücksichtigung der umliegenden Karos und der Sub-Karo-Position der Zielkoordinaten ein skaliertes Straße-/Nicht-Straße-Muster ausgewählt. Es entstehen so 256 mögliche Anordnungen mit teilweise noch unterschiedlichen Ergbnissen jenachdem ob man "oben-links", "mittig", oder woanders innerhalb des Karo ist. Diese Anordnungen und Subfälle wurden alle händisch analysiert und anschließend für jede Kombination dann ebenfalls händisch ein entsprechendes Straße-/Nicht-Straße-Muster programmiert. | ||
+ | |||
+ | Im Detail läuft das so ob: | ||
+ | |||
+ | # für die Zielkoordinaten (ganzzahlig) werden die folgenden Werte berechnet: | ||
+ | #* Ursprungskoordinaten (Kommazahl) | ||
+ | #* Zone innerhalb des Ursprungskaros = unterteilt nach ''north'', ''south'', ''east'', ''west'' | ||
+ | #* Ecke innerhalb des Ursprungskaros = unterteilt nach ''northwest'', ''northeast'', ''southeast'', ''southwest'' und ''center'' | ||
+ | #* Maske der umliegenden Pixel = 1-Bit pro umliegenden Karo startend von "obenlinks", über "oben", "obenrechts", ... im Uhrzeigersinn, bis "links" | ||
+ | #* Modulo Wert des Zielkaros = es wird quasi ein 1-Karo-Schachbrett über die ganze Zielkarte aufgespannt und die schwarzen bekommen 1 und die weißen 0 als Wert | ||
+ | # alle möglichen 256 Masken werden in einem riesigen Switch-Case-Statement abgebildet, wobei jedoch einige Anordnungen zum selben Ergebnis führen und demnach nicht alle einzeln runterprogrammiert sind | ||
+ | # für jede Maske gibt es dann bis zu zwei If-Statements, in denen anhand der Zone und/oder Ecke entschieden wird, welchen Wert das Karo bekommt. Zur Auswahl stehen: | ||
+ | #* der Center-Wert (also das Karo anhand Nearest-Neighbor-Algorithmus) | ||
+ | #* der Nachbar-Wert entsprechend der Seite, wo das Karo näher dran ist (so entstehen softere Diagonalen) | ||
+ | #* der Schachbrett-Wert (der wiederum anhand des Modulo-Werts alternatierend Center-Wert oder Nachbar-Wert zurück gibt) | ||
== Trivia == | == Trivia == | ||
Zeile 44: | Zeile 64: | ||
== Beispielkarten == | == Beispielkarten == | ||
− | + | Hier zur Veranschaulichung mal die Gegenüberstellung einiger Plagiate mit unterschiedlichen Parametern... Unterschiede zwischen 1 und 2 sind fett hervorgehoben | |
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
− | ! | + | ! Plagiat 1 !! Beschreibung 1 !! Plagiat 2 !! Beschreibung 2 |
+ | |- | ||
+ | | [[Bild:Plagiator_1_x2y2_nn.png|500px]] || MID = {{Karte|1}}<br/>scaleX = 2<br/>scaleY = 2<br/>'''scaler = nearestneighbor''' || [[Bild:Plagiator_1_x2y2_u.png|500px]] || MID = {{Karte|1}}<br/>scaleX = 2<br/>scaleY = 2<br/>'''scaler = ultimate''' | ||
+ | |- | ||
+ | | [[Bild:Plagiator_1_x3y1_nn_90_sf.png|500px]] || MID = {{Karte|1}}<br/>scaleX = 3<br/>scaleY = 1<br/>scaler = nearestneighbor<br/>'''rotation = 90'''<br/>'''Start Ziel getauscht''' || [[Bild:Plagiator_1_x3y1_nn_66.png|500px]] || MID = {{Karte|1}}<br/>scaleX = 3<br/>scale1 = 2<br/>scaler = nearestneighbor<br/>'''rotation = 66'''<br/>'''getrimmt''' | ||
+ | |- | ||
+ | | [[Bild:Plagiator_28_x2y2_nn.png]] || MID = {{Karte|28}}<br/>scaleX = 2<br/>scaleY = 2<br/>'''scaler = nearestneighbor''' || [[Bild:Plagiator_28_x2y2_u.png]] || MID = {{Karte|28}}<br/>scaleX = 2<br/>scaleY = 2<br/>'''scaler = ultimate''' | ||
|- | |- | ||
− | | [[Bild: | + | | [[Bild:Plagiator_28_x1.5y1.5_nn_135.png]] || MID = {{Karte|28}}<br/>scaleX = 1.5<br/>scaleY = 1.5<br/>'''scaler = nearestneighbor'''<br/>rotation = 135 || [[Bild:Plagiator_28_x1.5y1.5_u_135.png]] || MID = {{Karte|28}}<br/>scaleX = 1.5<br/>scaleY = 1.5<br/>'''scaler = ultimate'''<br/>rotation = 135 |
|} | |} | ||
[[Kategorie:Kartengenerator]] | [[Kategorie:Kartengenerator]] |
Aktuelle Version vom 29. Mai 2024, 14:36 Uhr
Plagiator ist ein von ultimate geschriebener Kartengenerator. Seit dem <tbd> ist er in Karopapier eingebunden.
Der Generator ist kein "normaler" Generator in dem Sinne, dass er eine Strecke "aus dem Nichts" erzeugt. Stattdessen erzeugt er Karten durch "Plagiieren", d.h. Transformieren und leichtes Verändern, existierender Karten. Zur Verfügung stehen die Transformationen Skalierung (unabhängig in X- und Y-Richtung, einschließlich Spiegeln) sowie Rotation. Zu den leichten Veränderungen zählen Start und Ziel tauschen und Trimmen. Bei einer Skalierung kann zudem zwischen zwei Algorithmen gewählt werden.
Inhaltsverzeichnis
Funktionsweise
Der Plagiator arbeitet nach der folgenden Funktionsweise:
- Laden des Karten-Codes aus der Karodatenbank
- (optional) Tauschen von Start und Ziel
- Erzeugen einer 3x3 Transformations-Matrix aus den Parametern Größe X, Größe Y und Rotation
- Kompensation des ggf. durch Rotation entstehenden Offsets
- Berechnung der Inversen Matrix
- Karoweise transformation der Karte anhand des entsprechenden Skalierungsalgorithmus
- (optional) Trimmen der Karte
Parameter
Plagiator kann durch folgende Parameter beeinflusst werden:
- Karte - MID der zu plagiierenden/transformierenden Karte. Hinweis: aus offensichtlichen Gründen können keine Nachtkarten transformiert werden.
- Größe X & Größe Y - Skalierungsfaktor in der jeweiligen Richtung; Faktoren mit |scale| > 1 vergrößern die Karte; Faktoren mit |scale| < 1 verkleinern sie; negativen Faktoren spiegeln die Karte entlang der entsprechenden Achse; 0 ist nicht erlaubt
- Rotation - Drehung der Karte im Uhrzeigersinn in Grad; es sind beliebige Winkel zulässig, für die bessere Bedienbarkeit sind im Frontend jedoch Winkel die Vielfache von 45° sind mit Tickmarks leichter anwählbar
- Skalierungsmodus - zur Auswahl stehen zwei Modi: ultimate und nearestneighbor. Details dazu unter Skalierungsalgorithmen
- Start und Ziel tauschen - ist dieses Flag gesetzt werden alle Startfelder durch Zielfelder ausgetauscht und umgekehrt. Hinweis: dadurch ändert sich ggf. die Anzahl der Startplätze
- Trimmen - normalerweise werden Eckbereiche, die bei der Rotation entstehend durch fortsetzen des Geländes aufgefüllt. Möchte man dies nicht, kann man die Karte trimmen lassen. Die Karte wird dann soweit verkleinert, dass nur noch ein 1-Karo-breiter, nicht-befahrbarer Rand um die Strecke übrig bleibt (ggf. kann dies auch Karten ohne Rotation kleiner machen, wenn diese mehr Rand haben)
Skalierungsalgorithmen
Die Herausforderung beim Skalieren von Karten besteht darin sicherzustellen, dass das Ergebnis einerseits optisch ansprechend, aber trotzdem auch spannend zu fahren ist. Dazu habe ich (ultimate) ein bisschen rumprobiert und zwei unterschiedliche Algorithmen implementiert, die man enstprechend auswählen kann.
(Beispiele siehe #Beispielkarten)
nearestneighbor
Der Nearest-Neighbor-Algorithmus ist ein aus der Bildverarbeitung bekannter Algorithmus. Die Zielkoordinaten werden einfach mit dem Skalierungsfaktor verrechnet und vom Ergebnis Nachkommastellen abgeschnitten um auf die Ursprungskoordinaten zu kommen. Bei Faktor 2 wird also beispielsweise jedes Karo durch 2x2 identische Karos ersetzt...
ultimate
Da bei Verwendung des Nearest-Neighbor-Algorithmus zwangsläufig "langweilige" Kartenelemente entstehen können - wie z.B. einfach nur größere Versionen von schachbrettartigen Stellen, wollte ich einen Algorithmus bauen, der für Karopapier optimierte Ergebnisse ausgibt, die spannender sind. Dazu wird bei der Skalierung zwischen Straßenfeldern (also Asphalt, CPs, Start, Ziel) und nicht-Straßenfeldern (Gras, Sand, Wasser, etc.) unterschieden und unter der Berücksichtigung der umliegenden Karos und der Sub-Karo-Position der Zielkoordinaten ein skaliertes Straße-/Nicht-Straße-Muster ausgewählt. Es entstehen so 256 mögliche Anordnungen mit teilweise noch unterschiedlichen Ergbnissen jenachdem ob man "oben-links", "mittig", oder woanders innerhalb des Karo ist. Diese Anordnungen und Subfälle wurden alle händisch analysiert und anschließend für jede Kombination dann ebenfalls händisch ein entsprechendes Straße-/Nicht-Straße-Muster programmiert.
Im Detail läuft das so ob:
- für die Zielkoordinaten (ganzzahlig) werden die folgenden Werte berechnet:
- Ursprungskoordinaten (Kommazahl)
- Zone innerhalb des Ursprungskaros = unterteilt nach north, south, east, west
- Ecke innerhalb des Ursprungskaros = unterteilt nach northwest, northeast, southeast, southwest und center
- Maske der umliegenden Pixel = 1-Bit pro umliegenden Karo startend von "obenlinks", über "oben", "obenrechts", ... im Uhrzeigersinn, bis "links"
- Modulo Wert des Zielkaros = es wird quasi ein 1-Karo-Schachbrett über die ganze Zielkarte aufgespannt und die schwarzen bekommen 1 und die weißen 0 als Wert
- alle möglichen 256 Masken werden in einem riesigen Switch-Case-Statement abgebildet, wobei jedoch einige Anordnungen zum selben Ergebnis führen und demnach nicht alle einzeln runterprogrammiert sind
- für jede Maske gibt es dann bis zu zwei If-Statements, in denen anhand der Zone und/oder Ecke entschieden wird, welchen Wert das Karo bekommt. Zur Auswahl stehen:
- der Center-Wert (also das Karo anhand Nearest-Neighbor-Algorithmus)
- der Nachbar-Wert entsprechend der Seite, wo das Karo näher dran ist (so entstehen softere Diagonalen)
- der Schachbrett-Wert (der wiederum anhand des Modulo-Werts alternatierend Center-Wert oder Nachbar-Wert zurück gibt)
Trivia
- der Plagiator arbeitet 100% deterministisch, d.h. es ist kein Zufall im Spiel und bei gleichen Parameter kommt auch immer das gleiche Ergebnis raus
- aufgrund des deterministischen Verhaltens können mit dem Plagiator keine Nachtkarten erzeugt werden (man könnte sonst schummeln). Um dennoch zufällige (Nacht)-Karten fahren zu können,
- es kann sein, dass bestimmte Parameterkombinationen zu nicht fahrbaren Karten führen - Ursachen für nicht fahrbare Karten können sein:
- Verkleinerung oder Rotation macht Strecke unpassierbar (ggf. abhängig vom Skalierungsalgorithmus)
- Tausch von Start und Ziel macht Route für einige Spieler unpassierbar (z.B. weil es dekorative Ziele gibt, die dann zu nicht fahrbaren Starts führen)
Beispielkarten
Hier zur Veranschaulichung mal die Gegenüberstellung einiger Plagiate mit unterschiedlichen Parametern... Unterschiede zwischen 1 und 2 sind fett hervorgehoben
Plagiat 1 | Beschreibung 1 | Plagiat 2 | Beschreibung 2 |
---|---|---|---|
MID = 1 scaleX = 2 scaleY = 2 scaler = nearestneighbor |
MID = 1 scaleX = 2 scaleY = 2 scaler = ultimate | ||
MID = 1 scaleX = 3 scaleY = 1 scaler = nearestneighbor rotation = 90 Start Ziel getauscht |
MID = 1 scaleX = 3 scale1 = 2 scaler = nearestneighbor rotation = 66 getrimmt | ||
MID = 28 scaleX = 2 scaleY = 2 scaler = nearestneighbor |
MID = 28 scaleX = 2 scaleY = 2 scaler = ultimate | ||
MID = 28 scaleX = 1.5 scaleY = 1.5 scaler = nearestneighbor rotation = 135 |
MID = 28 scaleX = 1.5 scaleY = 1.5 scaler = ultimate rotation = 135 |