Version 1.0

This commit is contained in:
Aaron Fischer 2021-03-21 00:07:57 +01:00
parent 3d58303936
commit 3871273730
11 changed files with 197 additions and 33 deletions

12
Dockerfile Normal file
View File

@ -0,0 +1,12 @@
FROM golang:1.16.2-alpine AS builder
RUN apk update
WORKDIR /app
COPY . /app
RUN GOOS=linux go build -o kartograph cmd/kartograph-map-generator/main.go
FROM alpine:latest
RUN apk update
WORKDIR /app
COPY --from=builder /app/kartograph /app/
EXPOSE 80
ENTRYPOINT ["/app/kartograph"]

34
README.mkd Normal file
View File

@ -0,0 +1,34 @@
# Der Kartograph Map Generator
Generate your own maps for "Der Kartograph" easily.
## Build
You need to build the source first. The simplest way is to use the
provided Dockerfile, which will build the software and create a
convenient image (This is a multi stage Dockerfile, so the resulting
image is a small Alpine Linux image with ust the map generator binary.)
$ docker build -t kartograph .
## Run
See all available options:
$ docker run -it kartograph -help
Spawn a webserver:
$ docker run -it kartograph -web
Generate a map with the seed "hey jim" and output it as a SVG image:
$ docker run -it kartograph -output=svg -seed='hey jim' > map.svg
You want an epic game? Lets generate a big map with a lot of wasteland
and ruins:
$ docker run -it kartograph -seed=aaron \
-size=20 -wastelands=100 -ruins=10 \
-output=svg > map.svg

View File

@ -45,7 +45,7 @@ func main() {
func init() { func init() {
flag.BoolVar(&startWebserver, "web", false, "Spawn a webserver") flag.BoolVar(&startWebserver, "web", false, "Spawn a webserver")
flag.StringVar(&address, "address", "0.0.0.0", "IP to bin the service to") flag.StringVar(&address, "address", "0.0.0.0", "IP to bind the service to")
flag.IntVar(&port, "port", 80, "Port to bind the service to") flag.IntVar(&port, "port", 80, "Port to bind the service to")
flag.StringVar(&output, "output", "svg", "Output format (svg, ascii, json)") flag.StringVar(&output, "output", "svg", "Output format (svg, ascii, json)")

1
go.mod
View File

@ -4,5 +4,4 @@ go 1.16
require ( require (
github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb
github.com/jung-kurt/gofpdf v1.16.2
) )

View File

@ -1,7 +0,0 @@
package generator
import ()
func (w World) PDF() {
}

View File

@ -9,6 +9,7 @@ import (
"io" "io"
"math" "math"
"math/rand" "math/rand"
"time"
) )
type TerritoryType int type TerritoryType int
@ -69,7 +70,7 @@ type World struct {
Mountains int `json:"mountains"` Mountains int `json:"mountains"`
World []Tile `json:"tiles"` World []Tile `json:"tiles"`
Seed int Seed string
} }
func (w World) Plot() string { func (w World) Plot() string {
@ -90,23 +91,15 @@ func (w World) JSON() string {
return string(output) return string(output)
} }
func (w World) ExportToPDF(filename string) error {
// TODO: Draw the PDF
return nil
}
func New(size int, numWastelands int, numMountains int, numRuins int, seed string) World { func New(size int, numWastelands int, numMountains int, numRuins int, seed string) World {
// Fix seed InitSeed(seed)
h := md5.New()
io.WriteString(h, seed)
var intSeed uint64 = binary.BigEndian.Uint64(h.Sum(nil))
rand.Seed(int64(intSeed))
w := World{ w := World{
Size: size, Size: size,
Wastelands: numWastelands, Wastelands: numWastelands,
Mountains: numMountains, Mountains: numMountains,
Ruins: numRuins, Ruins: numRuins,
Seed: seed,
} }
// All empty for start // All empty for start
@ -174,6 +167,24 @@ func New(size int, numWastelands int, numMountains int, numRuins int, seed strin
return w return w
} }
func InitSeed(seed string) {
h := md5.New()
io.WriteString(h, seed)
var intSeed uint64 = binary.BigEndian.Uint64(h.Sum(nil))
rand.Seed(int64(intSeed))
}
func RandomSeed() string {
InitSeed(time.Now().String())
chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
var seed []byte
for i := 0; i < 10; i++ {
seed = append(seed, chars[roll(25)])
}
return string(seed)
}
func (w World) neighbour(direction int, pos int) (int, error) { func (w World) neighbour(direction int, pos int) (int, error) {
switch direction { switch direction {
case UpDirection: case UpDirection:

View File

@ -41,15 +41,15 @@ func mapHandler(w http.ResponseWriter, req *http.Request) {
if seed == "" { if seed == "" {
seed = time.Now().String() seed = time.Now().String()
} }
wastelands, err := strconv.Atoi(req.URL.Query().Get("wastelands")) wastelands, err := strconv.Atoi(req.URL.Query().Get("w"))
if err != nil { if err != nil {
wastelands = 7 wastelands = 7
} }
mountains, err := strconv.Atoi(req.URL.Query().Get("mountains")) mountains, err := strconv.Atoi(req.URL.Query().Get("m"))
if err != nil { if err != nil {
mountains = 5 mountains = 5
} }
ruins, err := strconv.Atoi(req.URL.Query().Get("ruins")) ruins, err := strconv.Atoi(req.URL.Query().Get("r"))
if err != nil { if err != nil {
ruins = 6 ruins = 6
} }
@ -63,12 +63,15 @@ func mapHandler(w http.ResponseWriter, req *http.Request) {
} }
func indexHandler(w http.ResponseWriter, req *http.Request) { func indexHandler(w http.ResponseWriter, req *http.Request) {
log.Printf("GET / (%v)", req.RemoteAddr) seed := generator.RandomSeed()
world := generator.New(11, 7, 5, 6, seed)
log.Printf("GET / (%v) Seed: %v", req.RemoteAddr, seed)
tpl, err := template.ParseFS(templateFiles, "templates/index.tpl.html") tpl, err := template.ParseFS(templateFiles, "templates/index.tpl.html")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
tpl.Execute(w, nil) tpl.Execute(w, world)
} }

View File

@ -0,0 +1,51 @@
document.addEventListener('DOMContentLoaded', function() {
let generate = function(seed) {
let size = document.querySelector('input[name="size"]').value
let wastelands = document.querySelector('input[name="wastelands"]').value
let mountains = document.querySelector('input[name="mountains"]').value
let ruins = document.querySelector('input[name="ruins"]').value
let map = document.querySelector('.map')
// Generate new random seed
let newMapUrl = '/map.svg?seed=' + seed +
'&s=' + size +
'&w=' + wastelands +
'&m=' + mountains +
'&r=' + ruins
document.querySelector('.map').setAttribute('src', newMapUrl);
document.querySelector('#map-link').setAttribute('href', newMapUrl);
};
let buttons = document.querySelectorAll('input[type="button"]')
Array.prototype.forEach.call(buttons, function(button, i) {
button.addEventListener('click', function(e) {
// Reset to default buttons
if (e.target.hasAttribute('data-default')) {
let defaultValue = e.target.getAttribute('data-default');
let name = e.target.getAttribute('name').substr(8);
let targetInput = document.querySelector('input[type="text"][name="'+name+'"]')
targetInput.value = defaultValue;
}
// Roll the dice
if (e.target.getAttribute('name') === 'random-seed') {
let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let seed = '';
for (let i=0; i<10; i++) {
let index = Math.floor(Math.random() * 25)
seed += chars[index]
}
let seedElement = document.querySelector('input[name="seed"]');
seedElement.value = seed;
generate(seed);
}
// Refresh
if (e.target.getAttribute('name') === 'refresh') {
let seed = document.querySelector('input[name="seed"]').value;
generate(seed);
}
})
});
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -1,10 +1,38 @@
body {
width: 900px;
margin: auto;
}
h1 {
text-align: center;
}
p > img { p > img {
float: right; float: left;
width: 250px; width: 100px;
margin: 10px;
} }
.map { .map {
margin: auto; float: right;
display: block; width: 450px;
width: 350px; margin: 20px;
}
label {
width: 100px;
display: inline-block;
text-align: right;
}
input[type="text"] {
width: 90px;
}
input[name="seed"] {
width: 250px;
}
fieldset {
width: 370px;
margin: 10px 0;
} }

View File

@ -7,9 +7,11 @@
<meta name="description" content=""> <meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/static/styles.css" rel="stylesheet" type="text/css" media="all"> <link href="/static/styles.css" rel="stylesheet" type="text/css" media="all">
<script src="/static/generator.js"></script>
</head> </head>
<body> <body>
<h1>Map Generator für "Der Kartograph"</h1> <h1>Map Generator für "Der Kartograph"</h1>
<a id="map-link" href="/map.svg?seed={{ .Seed }}&s={{ .Size }}&w={{ .Wastelands }}&r={{ .Ruins }}&m={{ .Mountains }}"><img src="/map.svg?seed={{ .Seed }}&s={{ .Size }}&w={{ .Wastelands }}&r={{ .Ruins }}&m={{ .Mountains }}" class="map"></a>
<p><img src="/static/images/logo.jpg"> <p><img src="/static/images/logo.jpg">
Wem der beigelegte Block zu eintönig wird oder schon leergespielt hat, kann sich Wem der beigelegte Block zu eintönig wird oder schon leergespielt hat, kann sich
entweder die Mini-Erweiterung kaufen, in dem ein weiterer Block enthalten ist, entweder die Mini-Erweiterung kaufen, in dem ein weiterer Block enthalten ist,
@ -17,8 +19,39 @@
Die generierten Karten entsprechen den erweiterten Regeln, können aber für etwas Die generierten Karten entsprechen den erweiterten Regeln, können aber für etwas
mehr Spaß angepasst werden.</p> mehr Spaß angepasst werden.</p>
<a href="/">Neue Map generieren</a> {{ with . }}
<br><br> <form>
<img src="/map.svg" class="map"> <fieldset>
<legend>Seed</legend>
<input type="text" name="seed" value="{{ .Seed }}">
<input type="button" name="random-seed" value="Zufall">
</fieldset>
<fieldset>
<legend>Spielfeldgröße</legend>
<input type="text" name="size" value="{{ .Size }}">
<input type="button" name="default-size" value="Standard (11)" class="default-button" data-default="11">
</fieldset>
<fieldset>
<legend>Parameter</legend>
<label for="wastelands">Ödland:</label>
<input type="text" name="wastelands" value="{{ .Wastelands }}">
<input type="button" name="default-wastelands" value="Standard (7)" class="default-button" data-default="7">
<br>
<label for="mountains">Berge:</label>
<input type="text" name="mountains" value="{{ .Mountains }}">
<input type="button" name="default-mountains" value="Standard (5)" class="default-button" data-default="5">
<br>
<label for="ruins">Ruinen:</label>
<input type="text" name="ruins" value="{{ .Ruins }}">
<input type="button" name="default-ruins" value="Standard (6)" class="default-button" data-default="6">
<br>
</fieldset>
<input type="button" name="refresh" value="Neu generieren">
<input type="button" name="print" value="Drucken">
</form>
{{ end }}
</body> </body>
</html> </html>