From 9255872f57ad4d8e0f49f9e2ee96d71469598227 Mon Sep 17 00:00:00 2001 From: Aaron Fischer Date: Tue, 9 Mar 2021 11:29:46 +0100 Subject: [PATCH] First working SVG output --- cmd/kartograph-map-generator/main.go | 15 +- go.mod | 11 +- go.sum | 109 +------------ pkg/generator/generator.go | 10 -- pkg/generator/svg.go | 100 ++++++++++++ pkg/generator/world.go | 226 +++++++++++++++++++++++++++ 6 files changed, 340 insertions(+), 131 deletions(-) delete mode 100644 pkg/generator/generator.go create mode 100644 pkg/generator/svg.go create mode 100644 pkg/generator/world.go diff --git a/cmd/kartograph-map-generator/main.go b/cmd/kartograph-map-generator/main.go index 921db35..195c28e 100644 --- a/cmd/kartograph-map-generator/main.go +++ b/cmd/kartograph-map-generator/main.go @@ -1,6 +1,13 @@ -package generator +package main -type Field struct { - X int - Y int +import ( + "f00860/kartograph-map-editor/pkg/generator" + "fmt" +) + +func main() { + world := generator.New(11, 7, 5, 6) + //world := generator.New(13, 50, 5, 6) + fmt.Print(world.Plot()) + world.SVG("/tmp/test.svg") } diff --git a/go.mod b/go.mod index 8697403..27b7cee 100644 --- a/go.mod +++ b/go.mod @@ -2,13 +2,4 @@ module f00860/kartograph-map-editor go 1.15 -require ( - github.com/mattn/go-runewidth v0.0.10 // indirect - github.com/motemen/gore v0.5.2 // indirect - github.com/peterh/liner v1.2.1 // indirect - golang.org/x/mod v0.4.1 // indirect - golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 // indirect - golang.org/x/text v0.3.5 // indirect - golang.org/x/tools/gopls v0.6.6 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect -) +require github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb diff --git a/go.sum b/go.sum index 595975b..658a07a 100644 --- a/go.sum +++ b/go.sum @@ -1,107 +1,2 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU= -github.com/jba/templatecheck v0.5.0/go.mod h1:/1k7EajoSErFI9GLHAsiIJEaNLt3ALKNw2TV7z2SYv4= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/motemen/go-quickfix v0.0.0-20200118031250-2a6e54e79a50 h1:LKXuJ8pSlacZRgcNFoN2OHehByrDxJesflgC/tH7aBM= -github.com/motemen/go-quickfix v0.0.0-20200118031250-2a6e54e79a50/go.mod h1:8l73QMUlCqIlm+7YUTMwzUS4UH4gQqigx7toFASjmu0= -github.com/motemen/gore v0.5.2 h1:MXcPj3myuuAToh1EaZbfYworggO7wEva6C49TOGqltA= -github.com/motemen/gore v0.5.2/go.mod h1:LLl0jKyT5KKWhFV5VwhcR1/D/obZk8NnZxRWZcHqZUg= -github.com/peterh/liner v1.2.1 h1:O4BlKaq/LWu6VRWmol4ByWfzx6MfXc5Op5HETyIy5yg= -github.com/peterh/liner v1.2.1/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/sanity-io/litter v1.3.0 h1:5ZO+weUsqdSWMUng5JnpkW/Oz8iTXiIdeumhQr1sSjs= -github.com/sanity-io/litter v1.3.0/go.mod h1:5Z71SvaYy5kcGtyglXOC9rrUi3c1E8CamFWjQsazTh0= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM= -golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 h1:8qxJSnu+7dRq6upnbntrmriWByIakBuct5OM/MdQC1M= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1-0.20210222172741-77e031214674 h1:XzEjy9Ks1MwmcJOarbxTnL/AqHtzsRfRzAwfDhII2lE= -golang.org/x/tools v0.1.1-0.20210222172741-77e031214674/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= -golang.org/x/tools/gopls v0.6.6 h1:GmCsAKZMEb1BD1BTWnQrMyx4FmNThlEsmuFiJbLBXio= -golang.org/x/tools/gopls v0.6.6/go.mod h1:tPKjrE4Gnb7tGA4fXBGqRP6b7OfI5W4rvTNKmnHb6V0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.1.1 h1:EVDuO03OCZwpV2t/tLLxPmPiomagMoBOgfPt0FM+4IY= -honnef.co/go/tools v0.1.1/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -mvdan.cc/gofumpt v0.1.0 h1:hsVv+Y9UsZ/mFZTxJZuHVI6shSQCtzZ11h1JEFPAZLw= -mvdan.cc/gofumpt v0.1.0/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48= -mvdan.cc/xurls/v2 v2.2.0 h1:NSZPykBXJFCetGZykLAxaL6SIpvbVy/UFEniIfHAa8A= -mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8= +github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb h1:EVl3FJLQCzSbgBezKo/1A4ADnJ4mtJZ0RvnNzDJ44nY= +github.com/ajstarks/svgo v0.0.0-20200725142600-7a3c8b57fecb/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= diff --git a/pkg/generator/generator.go b/pkg/generator/generator.go deleted file mode 100644 index 9caf001..0000000 --- a/pkg/generator/generator.go +++ /dev/null @@ -1,10 +0,0 @@ -package generator - -type Field struct { - X int - Y int -} - -func Generate() { - fmt. -} diff --git a/pkg/generator/svg.go b/pkg/generator/svg.go new file mode 100644 index 0000000..5f23310 --- /dev/null +++ b/pkg/generator/svg.go @@ -0,0 +1,100 @@ +package generator + +import ( + "math" + "os" + + "github.com/ajstarks/svgo" +) + +func (w World) SVG(filename string) { + // DIN-A5 + width := 148 * 3 + height := 210 * 3 + + hPadding := 10 + vPadding := 60 + + gridsize := int(math.Floor(float64(width-2*hPadding) / float64(w.Size))) + boardsize := gridsize * w.Size + + file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0600) + if err != nil { + panic(err) + } + defer file.Close() + + canvas := svg.New(file) + + canvas.Start(width, height) + + canvas.Def() + canvas.Pattern("hatch00", 0, 0, 10, 10, "user") + canvas.Gstyle("fill:none; stroke:black; stroke-width:1.5") + canvas.Path("M0,0 l10,10") + canvas.Path("M10,0 l-10,10") + canvas.Gend() + canvas.PatternEnd() + canvas.DefEnd() + + canvas.Rect(0, 0, width, height, "fill:none; stroke:black") + + // Name, date + canvas.Rect(hPadding, 13, 170, 32, "fill:none; stroke:black") + canvas.Rect(hPadding+180, 13, 120, 32, "fill:none; stroke:black") + + // Final score + canvas.Roundrect(348, 10, 80, 40, 8, 8, "fill:none; stroke:black") + + // Grid + for i := 1; i < w.Size; i++ { + canvas.Line(hPadding, i*gridsize+vPadding, boardsize+hPadding, i*gridsize+vPadding, "fill:none; stroke:gray; stroke-dasharray:5,5") + canvas.Line(i*gridsize+hPadding, vPadding, i*gridsize+hPadding, boardsize+vPadding, "fill:none; stroke:gray; stroke-dasharray:5,5") + } + + // Terrain + for pos, tile := range w.World { + x, y := w.ToXY(pos) + switch tile.Territory { + case RuinsTerritory: + canvas.Text(hPadding+x*gridsize+3, vPadding+(y*gridsize)+16, "R", "stroke:lightgrey;fill:lightgrey") + case MountainTerritory: + canvas.Rect(hPadding+x*gridsize, vPadding+y*gridsize, gridsize, gridsize, "fill:white; stroke:black") + canvas.Circle(hPadding+x*gridsize+gridsize/2, vPadding+y*gridsize+gridsize/2, gridsize/2-8, "fill:white; stroke:black") + case WastelandTerritory: + canvas.Rect(hPadding+x*gridsize, vPadding+y*gridsize, gridsize, gridsize, "fill:white") + canvas.Rect(hPadding+x*gridsize, vPadding+y*gridsize, gridsize, gridsize, "fill:url(#hatch00)") + } + } + canvas.Rect(hPadding, vPadding, boardsize, boardsize, "fill:none; stroke:black; stroke-width:2") + + // Money bar + for i := 0; i < 14; i++ { + canvas.Circle(hPadding+i*30+12, vPadding+boardsize+30, 12, "fill:none; stroke:black") + } + + // Point boxes + for i := 0; i < 4; i++ { + startX := hPadding + i*95 + i*13 + startY := vPadding + boardsize + 80 + canvas.Roundrect(startX, startY, 95, 60, 8, 8, "fill:none; stroke:black") + canvas.Line(startX, startY+30, startX+60, startY+30, "fill:none; stroke:black") + canvas.Line(startX+30, startY, startX+30, startY+60, "fill:none; stroke:black") + canvas.Line(startX+60, startY, startX+60, startY+60, "fill:none; stroke:black") + } + + // Labels + canvas.Text(hPadding+11, vPadding+boardsize+74, "A", "stroke:black;fill:black;font-size:14px") + canvas.Text(hPadding+41, vPadding+boardsize+74, "B", "stroke:black;fill:black;font-size:14px") + + canvas.Text(hPadding+118, vPadding+boardsize+74, "B", "stroke:black;fill:black;font-size:14px") + canvas.Text(hPadding+148, vPadding+boardsize+74, "C", "stroke:black;fill:black;font-size:14px") + + canvas.Text(hPadding+225, vPadding+boardsize+74, "C", "stroke:black;fill:black;font-size:14px") + canvas.Text(hPadding+255, vPadding+boardsize+74, "D", "stroke:black;fill:black;font-size:14px") + + canvas.Text(hPadding+334, vPadding+boardsize+74, "D", "stroke:black;fill:black;font-size:14px") + canvas.Text(hPadding+364, vPadding+boardsize+74, "A", "stroke:black;fill:black;font-size:14px") + + canvas.End() +} diff --git a/pkg/generator/world.go b/pkg/generator/world.go new file mode 100644 index 0000000..56204fc --- /dev/null +++ b/pkg/generator/world.go @@ -0,0 +1,226 @@ +package generator + +import ( + "errors" + "fmt" + "math" + "math/rand" + "time" +) + +type TerritoryType int + +const ( + EmptyTerritory TerritoryType = iota + WastelandTerritory + MountainTerritory + RuinsTerritory +) + +const ( + UpDirection int = iota + RightDirection + DownDirection + LeftDirection +) + +type Tile struct { + Territory TerritoryType +} + +func (t Tile) Plot() string { + switch t.Territory { + case EmptyTerritory: + return " " + case WastelandTerritory: + return "#" + case MountainTerritory: + return "M" + case RuinsTerritory: + return "I" + } + + return "?" +} + +type World struct { + Size int + Wastelands int + Ruins int + Mountains int + + World []Tile +} + +func (w World) Plot() string { + var board string + for i := 0; i < w.Size*w.Size; i++ { + board += fmt.Sprintf("[%v]", w.World[i].Plot()) + + if (i+1)%w.Size == 0 { + board += fmt.Sprintf("\n") + } + } + + return board +} + +func (w World) ExportToPDF(filename string) error { + // TODO: Draw the PDF + return nil +} + +func New(size int, numWastelands int, numMountains int, numRuins int) World { + rand.Seed(time.Now().UnixNano()) + + w := World{ + Size: size, + } + + // All empty for start + for i := 0; i < w.Size*w.Size; i++ { + w.World = append(w.World, Tile{Territory: EmptyTerritory}) + } + + // Place wasteland area by finding a suitable place to start + // and surround it with 6 more wastelands + var wastelands []int + + startPos := roll(w.Size-1) + roll(w.Size-1)*w.Size + w.place(WastelandTerritory, startPos) + wastelands = append(wastelands, startPos) + + for i := 0; i < numWastelands-1; i++ { + var candidates []int + + // Find all possible candidates (top, left, bottom, right), + // from all already places wastelands, then choose one at + // random and place it. + for _, wl := range wastelands { + for _, free := range w.neighboursOfType(wl, EmptyTerritory) { + if !contains(candidates, free) { + candidates = append(candidates, free) + } + } + } + + candidate := randomItem(candidates) + w.place(WastelandTerritory, candidate) + wastelands = append(wastelands, candidate) + } + + // Place 5 mountains on free tiles. We need to make sure that + // a mountain do not touch another mountain. + for i := 0; i < numMountains; i++ { + var candidates []int + + // Start with all free fields + for pos, tile := range w.World { + if tile.Territory == EmptyTerritory && + len(w.neighboursOfType(pos, MountainTerritory)) == 0 { + candidates = append(candidates, pos) + } + } + w.place(MountainTerritory, randomItem(candidates)) + } + + // Place 6 ruins. Same constraint as mountains apply here (no + // two ruins should not touch each other) + for i := 0; i < numRuins; i++ { + var candidates []int + + // Start with all free fields + for pos, tile := range w.World { + if tile.Territory == EmptyTerritory && + len(w.neighboursOfType(pos, RuinsTerritory)) == 0 { + candidates = append(candidates, pos) + } + } + w.place(RuinsTerritory, randomItem(candidates)) + } + + return w +} + +func (w World) neighbour(direction int, pos int) (int, error) { + switch direction { + case UpDirection: + if pos < w.Size { + return 0, errors.New("Out of bounds") + } + return pos - w.Size, nil + case RightDirection: + if pos%w.Size == w.Size-1 || + pos >= w.Size*w.Size-1 { + return 0, errors.New("Out of bounds") + } + return pos + 1, nil + case DownDirection: + if pos >= ((w.Size-1)*w.Size)-1 { + return 0, errors.New("Out of bounds") + } + return pos + w.Size, nil + case LeftDirection: + if pos%w.Size == 0 || + pos <= 0 { + return 0, errors.New("Out of bounds") + } + return pos - 1, nil + } + + return 0, errors.New("Wrong direction") +} + +func (w World) neighbours(pos int) []int { + var neighbours []int + + for i := 0; i < 4; i++ { + n, err := w.neighbour(i, pos) + if err == nil { + neighbours = append(neighbours, n) + } + } + + return neighbours +} + +func (w World) neighboursOfType(pos int, territory TerritoryType) []int { + var neighbours []int + + for _, n := range w.neighbours(pos) { + if w.World[n].Territory == territory { + neighbours = append(neighbours, n) + } + } + + return neighbours +} + +func (w World) ToXY(pos int) (int, int) { + return pos % w.Size, int(math.Floor((float64(pos) / float64(w.Size)))) +} + +func roll(w int) int { + return rand.Intn(w) +} + +func contains(s []int, e int) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} + +func randomItem(c []int) int { + rand.Shuffle(len(c), func(i, j int) { + c[i], c[j] = c[j], c[i] + }) + + return c[0] +} + +func (w World) place(territory TerritoryType, pos int) { + w.World[pos] = Tile{Territory: territory} +}