├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── Godeps ├── Godeps.json ├── Readme └── _workspace │ ├── .gitignore │ └── src │ ├── github.com │ ├── golang │ │ └── freetype │ │ │ ├── LICENSE │ │ │ ├── raster │ │ │ ├── geom.go │ │ │ ├── paint.go │ │ │ ├── raster.go │ │ │ └── stroke.go │ │ │ └── truetype │ │ │ ├── face.go │ │ │ ├── glyph.go │ │ │ ├── hint.go │ │ │ ├── opcodes.go │ │ │ └── truetype.go │ ├── gorilla │ │ └── handlers │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── compress.go │ │ │ └── handlers.go │ ├── llgcode │ │ └── draw2d │ │ │ ├── .gitignore │ │ │ ├── AUTHORS │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── draw2d.go │ │ │ ├── draw2dbase │ │ │ ├── README.md │ │ │ ├── curve.go │ │ │ ├── dasher.go │ │ │ ├── demux_flattener.go │ │ │ ├── flattener.go │ │ │ ├── line.go │ │ │ ├── stack_gc.go │ │ │ └── stroker.go │ │ │ ├── draw2dimg │ │ │ ├── README.md │ │ │ ├── fileutil.go │ │ │ ├── ftgc.go │ │ │ ├── ftpath.go │ │ │ ├── rgba_interpolation.go │ │ │ └── text.go │ │ │ ├── font.go │ │ │ ├── gc.go │ │ │ ├── matrix.go │ │ │ ├── path.go │ │ │ └── test │ └── pborman │ │ └── uuid │ │ ├── CONTRIBUTORS │ │ ├── LICENSE │ │ ├── dce.go │ │ ├── doc.go │ │ ├── hash.go │ │ ├── json.go │ │ ├── node.go │ │ ├── sql.go │ │ ├── time.go │ │ ├── util.go │ │ ├── uuid.go │ │ ├── version1.go │ │ └── version4.go │ └── golang.org │ └── x │ └── image │ ├── LICENSE │ ├── PATENTS │ ├── font │ └── font.go │ └── math │ └── fixed │ └── fixed.go ├── LICENSE ├── Makefile ├── bits └── bits.go ├── doc ├── max_speed.md └── ride_ids.md ├── exe_reader ├── add_comments_to_openrct_coords │ └── ride-tmp.go ├── element_hex_codes │ └── print_all_hex_values.go ├── print_tracks │ └── main.go ├── track_values.go └── track_values_test.go ├── genetic ├── files.go ├── genetic.go ├── genetic_test.go ├── get_latest_track │ └── main.go ├── get_old_experiments │ └── main.go ├── rct2.go ├── rct2_test.go ├── run_experiment │ └── main.go └── track_completer.go ├── geo ├── degree_test.go ├── geo.go ├── geo_test.go └── spatial_test.go ├── image ├── 2d.go ├── above.go └── above_runner │ └── main.go ├── interface.md ├── physics └── physics.go ├── profile ├── 1.profile └── 2.profile ├── readme.md ├── ride-rating-from-c ├── a.out ├── compiling.md ├── foobar ├── get_rating ├── get_rating.c └── get_rating.s ├── rideheights.jpg ├── ridenames.go ├── rides ├── blackwidow.td6 ├── generated_mine_train.go ├── hello.td4 ├── mine-train-gold-rush.roundtrip-thru-go.TD6 ├── mine-train-gold-rush.roundtrip.td6 ├── mine-train-gold-rush.td6 ├── mine-train-gold-rush.td6.decoded ├── mine_train.go ├── mischief.td6 ├── mischief.td6.out ├── raptor.td4 └── woodchip.td6 ├── rle ├── decode_td6 │ └── decode_td6.go ├── encode_td6 │ └── encode_td6.go ├── lib.go └── lib_test.go ├── scripts ├── compress.bash └── diff_tracks.bash ├── server ├── main.go ├── static │ ├── bootstrap-theme.min.css │ ├── bootstrap.min.css │ ├── jquery.min.js │ ├── readme.md │ └── three.min.js └── templates │ └── iteration.html ├── td4 └── td4.go ├── td6 ├── egress.go ├── lib_test.go ├── spatial.go └── td6.go ├── td6_runners └── excitement.go ├── tracks ├── branch_factor │ └── main.go ├── elements.go ├── segment.go ├── segmentfns.go ├── segmentfns_test.go ├── track.go └── track_test.go └── wip ├── compare.go ├── funparser.go ├── inspect.go ├── inspect_mine_train.go ├── printvalue.go ├── read_direction_change_configs.go ├── read_experiment.go ├── read_has_running_track.go ├── read_ride_97D4F2_table.go ├── read_ride_97D7C9_table.go ├── read_ride_97E3A8_table.go ├── read_ride_97E3AA_table.go ├── read_ride_97E3B6_table.go ├── read_ride_994A38_table.go ├── read_ride_configs.go ├── read_ride_heights.go ├── read_ride_ids.go ├── read_ride_table.go ├── read_track_table.go ├── ride_ratings ├── compute_var_198.go └── rct-rides ├── rides.cpp.txt ├── roundtrip_mine_train.go └── tmp.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.png 2 | *.dump 3 | 4 | # XXX should compile and run these properly instead of using "go run" so they 5 | # don't leave binaries lying around. 6 | exe_reader/print_tracks/print_tracks 7 | genetic/run_experiment/runners 8 | genetic/run_experiment/run_experiment 9 | genetic/get_old_experiments/get_old_experiments 10 | genetic/get_latest_track/get_latest_track 11 | server/server 12 | rle/decode_td6/decode_td6 13 | rle/encode_td6/encode_td6 14 | tracks/branch_factor/branch_factor 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - tip 5 | - 1.6 6 | - 1.5 7 | - 1.4 8 | 9 | script: 10 | - make test 11 | 12 | sudo: false 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We welcome new contributors! Here is a guide to getting started. 4 | 5 | 1. Check [the README][readme] - It has useful information about installing the 6 | project, running tests, adding new coasters, what's been done and what needs to 7 | get done. 8 | 9 | 2. At this point I'm more excited to have people excited about the project than 10 | worries about code style. There is [a test suite up on Travis][travis]. I've 11 | found especially with the track data and directional stuff that adding tests 12 | can help enforce correctness, but I don't care too much. 13 | 14 | 3. Ask questions! I will literally drop anything I am working on to help you 15 | understand what's going on in the code base and help you to get started with 16 | this project. Contact me at [kev@inburke.com][mailto] or open 17 | an issue here. 18 | 19 | 4. Not super familiar with Git or pull requests or whatever? Don't worry - just 20 | change the code and email me and I'll help you contribute a patch. 21 | 22 | [readme]: https://github.com/kevinburke/rct/blob/master/readme.md 23 | [travis]: https://travis-ci.org/kevinburke/rct 24 | [mailto]: mailto:kev@inburke.com 25 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/kevinburke/rct", 3 | "GoVersion": "go1.6", 4 | "GodepVersion": "v70", 5 | "Packages": [ 6 | "./..." 7 | ], 8 | "Deps": [ 9 | { 10 | "ImportPath": "github.com/golang/freetype/raster", 11 | "Comment": "release-120-gf29eb11", 12 | "Rev": "f29eb116deb328d02ee5c573f02d442ca67d5532" 13 | }, 14 | { 15 | "ImportPath": "github.com/golang/freetype/truetype", 16 | "Comment": "release-120-gf29eb11", 17 | "Rev": "f29eb116deb328d02ee5c573f02d442ca67d5532" 18 | }, 19 | { 20 | "ImportPath": "github.com/gorilla/handlers", 21 | "Rev": "a24b39a6a2c8a7af2fe664dd4573205c99904035" 22 | }, 23 | { 24 | "ImportPath": "github.com/llgcode/draw2d", 25 | "Comment": "v1-65-g9ffe0e7", 26 | "Rev": "9ffe0e7eb54c1b3d28eb45a24b578dc2d1bdb6b3" 27 | }, 28 | { 29 | "ImportPath": "github.com/llgcode/draw2d/draw2dbase", 30 | "Comment": "v1-65-g9ffe0e7", 31 | "Rev": "9ffe0e7eb54c1b3d28eb45a24b578dc2d1bdb6b3" 32 | }, 33 | { 34 | "ImportPath": "github.com/llgcode/draw2d/draw2dimg", 35 | "Comment": "v1-65-g9ffe0e7", 36 | "Rev": "9ffe0e7eb54c1b3d28eb45a24b578dc2d1bdb6b3" 37 | }, 38 | { 39 | "ImportPath": "github.com/pborman/uuid", 40 | "Rev": "dee7705ef7b324f27ceb85a121c61f2c2e8ce988" 41 | }, 42 | { 43 | "ImportPath": "golang.org/x/image/font", 44 | "Rev": "baddd3465a05d84a6d8d3507547a91cb188c81ea" 45 | }, 46 | { 47 | "ImportPath": "golang.org/x/image/math/fixed", 48 | "Rev": "baddd3465a05d84a6d8d3507547a91cb188c81ea" 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/golang/freetype/LICENSE: -------------------------------------------------------------------------------- 1 | Use of the Freetype-Go software is subject to your choice of exactly one of 2 | the following two licenses: 3 | * The FreeType License, which is similar to the original BSD license with 4 | an advertising clause, or 5 | * The GNU General Public License (GPL), version 2 or later. 6 | 7 | The text of these licenses are available in the licenses/ftl.txt and the 8 | licenses/gpl.txt files respectively. They are also available at 9 | http://freetype.sourceforge.net/license.html 10 | 11 | The Luxi fonts in the testdata directory are licensed separately. See the 12 | testdata/COPYING file for details. 13 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/golang/freetype/raster/geom.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Freetype-Go Authors. All rights reserved. 2 | // Use of this source code is governed by your choice of either the 3 | // FreeType License or the GNU General Public License version 2 (or 4 | // any later version), both of which can be found in the LICENSE file. 5 | 6 | package raster 7 | 8 | import ( 9 | "fmt" 10 | "math" 11 | 12 | "github.com/kevinburke/rct/Godeps/_workspace/src/golang.org/x/image/math/fixed" 13 | ) 14 | 15 | // maxAbs returns the maximum of abs(a) and abs(b). 16 | func maxAbs(a, b fixed.Int26_6) fixed.Int26_6 { 17 | if a < 0 { 18 | a = -a 19 | } 20 | if b < 0 { 21 | b = -b 22 | } 23 | if a < b { 24 | return b 25 | } 26 | return a 27 | } 28 | 29 | // pNeg returns the vector -p, or equivalently p rotated by 180 degrees. 30 | func pNeg(p fixed.Point26_6) fixed.Point26_6 { 31 | return fixed.Point26_6{-p.X, -p.Y} 32 | } 33 | 34 | // pDot returns the dot product p·q. 35 | func pDot(p fixed.Point26_6, q fixed.Point26_6) fixed.Int52_12 { 36 | px, py := int64(p.X), int64(p.Y) 37 | qx, qy := int64(q.X), int64(q.Y) 38 | return fixed.Int52_12(px*qx + py*qy) 39 | } 40 | 41 | // pLen returns the length of the vector p. 42 | func pLen(p fixed.Point26_6) fixed.Int26_6 { 43 | // TODO(nigeltao): use fixed point math. 44 | x := float64(p.X) 45 | y := float64(p.Y) 46 | return fixed.Int26_6(math.Sqrt(x*x + y*y)) 47 | } 48 | 49 | // pNorm returns the vector p normalized to the given length, or zero if p is 50 | // degenerate. 51 | func pNorm(p fixed.Point26_6, length fixed.Int26_6) fixed.Point26_6 { 52 | d := pLen(p) 53 | if d == 0 { 54 | return fixed.Point26_6{} 55 | } 56 | s, t := int64(length), int64(d) 57 | x := int64(p.X) * s / t 58 | y := int64(p.Y) * s / t 59 | return fixed.Point26_6{fixed.Int26_6(x), fixed.Int26_6(y)} 60 | } 61 | 62 | // pRot45CW returns the vector p rotated clockwise by 45 degrees. 63 | // 64 | // Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}. 65 | func pRot45CW(p fixed.Point26_6) fixed.Point26_6 { 66 | // 181/256 is approximately 1/√2, or sin(π/4). 67 | px, py := int64(p.X), int64(p.Y) 68 | qx := (+px - py) * 181 / 256 69 | qy := (+px + py) * 181 / 256 70 | return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)} 71 | } 72 | 73 | // pRot90CW returns the vector p rotated clockwise by 90 degrees. 74 | // 75 | // Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}. 76 | func pRot90CW(p fixed.Point26_6) fixed.Point26_6 { 77 | return fixed.Point26_6{-p.Y, p.X} 78 | } 79 | 80 | // pRot135CW returns the vector p rotated clockwise by 135 degrees. 81 | // 82 | // Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}. 83 | func pRot135CW(p fixed.Point26_6) fixed.Point26_6 { 84 | // 181/256 is approximately 1/√2, or sin(π/4). 85 | px, py := int64(p.X), int64(p.Y) 86 | qx := (-px - py) * 181 / 256 87 | qy := (+px - py) * 181 / 256 88 | return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)} 89 | } 90 | 91 | // pRot45CCW returns the vector p rotated counter-clockwise by 45 degrees. 92 | // 93 | // Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}. 94 | func pRot45CCW(p fixed.Point26_6) fixed.Point26_6 { 95 | // 181/256 is approximately 1/√2, or sin(π/4). 96 | px, py := int64(p.X), int64(p.Y) 97 | qx := (+px + py) * 181 / 256 98 | qy := (-px + py) * 181 / 256 99 | return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)} 100 | } 101 | 102 | // pRot90CCW returns the vector p rotated counter-clockwise by 90 degrees. 103 | // 104 | // Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}. 105 | func pRot90CCW(p fixed.Point26_6) fixed.Point26_6 { 106 | return fixed.Point26_6{p.Y, -p.X} 107 | } 108 | 109 | // pRot135CCW returns the vector p rotated counter-clockwise by 135 degrees. 110 | // 111 | // Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}. 112 | func pRot135CCW(p fixed.Point26_6) fixed.Point26_6 { 113 | // 181/256 is approximately 1/√2, or sin(π/4). 114 | px, py := int64(p.X), int64(p.Y) 115 | qx := (-px + py) * 181 / 256 116 | qy := (-px - py) * 181 / 256 117 | return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)} 118 | } 119 | 120 | // An Adder accumulates points on a curve. 121 | type Adder interface { 122 | // Start starts a new curve at the given point. 123 | Start(a fixed.Point26_6) 124 | // Add1 adds a linear segment to the current curve. 125 | Add1(b fixed.Point26_6) 126 | // Add2 adds a quadratic segment to the current curve. 127 | Add2(b, c fixed.Point26_6) 128 | // Add3 adds a cubic segment to the current curve. 129 | Add3(b, c, d fixed.Point26_6) 130 | } 131 | 132 | // A Path is a sequence of curves, and a curve is a start point followed by a 133 | // sequence of linear, quadratic or cubic segments. 134 | type Path []fixed.Int26_6 135 | 136 | // String returns a human-readable representation of a Path. 137 | func (p Path) String() string { 138 | s := "" 139 | for i := 0; i < len(p); { 140 | if i != 0 { 141 | s += " " 142 | } 143 | switch p[i] { 144 | case 0: 145 | s += "S0" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3])) 146 | i += 4 147 | case 1: 148 | s += "A1" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3])) 149 | i += 4 150 | case 2: 151 | s += "A2" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+5])) 152 | i += 6 153 | case 3: 154 | s += "A3" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+7])) 155 | i += 8 156 | default: 157 | panic("freetype/raster: bad path") 158 | } 159 | } 160 | return s 161 | } 162 | 163 | // Clear cancels any previous calls to p.Start or p.AddXxx. 164 | func (p *Path) Clear() { 165 | *p = (*p)[:0] 166 | } 167 | 168 | // Start starts a new curve at the given point. 169 | func (p *Path) Start(a fixed.Point26_6) { 170 | *p = append(*p, 0, a.X, a.Y, 0) 171 | } 172 | 173 | // Add1 adds a linear segment to the current curve. 174 | func (p *Path) Add1(b fixed.Point26_6) { 175 | *p = append(*p, 1, b.X, b.Y, 1) 176 | } 177 | 178 | // Add2 adds a quadratic segment to the current curve. 179 | func (p *Path) Add2(b, c fixed.Point26_6) { 180 | *p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2) 181 | } 182 | 183 | // Add3 adds a cubic segment to the current curve. 184 | func (p *Path) Add3(b, c, d fixed.Point26_6) { 185 | *p = append(*p, 3, b.X, b.Y, c.X, c.Y, d.X, d.Y, 3) 186 | } 187 | 188 | // AddPath adds the Path q to p. 189 | func (p *Path) AddPath(q Path) { 190 | *p = append(*p, q...) 191 | } 192 | 193 | // AddStroke adds a stroked Path. 194 | func (p *Path) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) { 195 | Stroke(p, q, width, cr, jr) 196 | } 197 | 198 | // firstPoint returns the first point in a non-empty Path. 199 | func (p Path) firstPoint() fixed.Point26_6 { 200 | return fixed.Point26_6{p[1], p[2]} 201 | } 202 | 203 | // lastPoint returns the last point in a non-empty Path. 204 | func (p Path) lastPoint() fixed.Point26_6 { 205 | return fixed.Point26_6{p[len(p)-3], p[len(p)-2]} 206 | } 207 | 208 | // addPathReversed adds q reversed to p. 209 | // For example, if q consists of a linear segment from A to B followed by a 210 | // quadratic segment from B to C to D, then the values of q looks like: 211 | // index: 01234567890123 212 | // value: 0AA01BB12CCDD2 213 | // So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A). 214 | func addPathReversed(p Adder, q Path) { 215 | if len(q) == 0 { 216 | return 217 | } 218 | i := len(q) - 1 219 | for { 220 | switch q[i] { 221 | case 0: 222 | return 223 | case 1: 224 | i -= 4 225 | p.Add1( 226 | fixed.Point26_6{q[i-2], q[i-1]}, 227 | ) 228 | case 2: 229 | i -= 6 230 | p.Add2( 231 | fixed.Point26_6{q[i+2], q[i+3]}, 232 | fixed.Point26_6{q[i-2], q[i-1]}, 233 | ) 234 | case 3: 235 | i -= 8 236 | p.Add3( 237 | fixed.Point26_6{q[i+4], q[i+5]}, 238 | fixed.Point26_6{q[i+2], q[i+3]}, 239 | fixed.Point26_6{q[i-2], q[i-1]}, 240 | ) 241 | default: 242 | panic("freetype/raster: bad path") 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/handlers/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.0 5 | - 1.1 6 | - 1.2 7 | - 1.3 8 | - 1.4 9 | - tip 10 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/handlers/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/handlers/README.md: -------------------------------------------------------------------------------- 1 | gorilla/handlers 2 | ================ 3 | [![Build Status](https://travis-ci.org/gorilla/handlers.png?branch=master)](https://travis-ci.org/gorilla/handlers) 4 | 5 | *Warning:* This package is a work in progress and the APIs are subject to change. 6 | Consider this a v0 project. 7 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/handlers/compress.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package handlers 6 | 7 | import ( 8 | "compress/flate" 9 | "compress/gzip" 10 | "io" 11 | "net/http" 12 | "strings" 13 | ) 14 | 15 | type compressResponseWriter struct { 16 | io.Writer 17 | http.ResponseWriter 18 | http.Hijacker 19 | } 20 | 21 | func (w *compressResponseWriter) Header() http.Header { 22 | return w.ResponseWriter.Header() 23 | } 24 | 25 | func (w *compressResponseWriter) Write(b []byte) (int, error) { 26 | h := w.ResponseWriter.Header() 27 | if h.Get("Content-Type") == "" { 28 | h.Set("Content-Type", http.DetectContentType(b)) 29 | } 30 | 31 | return w.Writer.Write(b) 32 | } 33 | 34 | func CompressHandler(h http.Handler) http.Handler { 35 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 36 | L: 37 | for _, enc := range strings.Split(r.Header.Get("Accept-Encoding"), ",") { 38 | switch strings.TrimSpace(enc) { 39 | case "gzip": 40 | w.Header().Set("Content-Encoding", "gzip") 41 | w.Header().Add("Vary", "Accept-Encoding") 42 | 43 | gw := gzip.NewWriter(w) 44 | defer gw.Close() 45 | 46 | h, hok := w.(http.Hijacker) 47 | if !hok { /* w is not Hijacker... oh well... */ 48 | h = nil 49 | } 50 | 51 | w = &compressResponseWriter{ 52 | Writer: gw, 53 | ResponseWriter: w, 54 | Hijacker: h, 55 | } 56 | 57 | break L 58 | case "deflate": 59 | w.Header().Set("Content-Encoding", "deflate") 60 | w.Header().Add("Vary", "Accept-Encoding") 61 | 62 | fw, _ := flate.NewWriter(w, flate.DefaultCompression) 63 | defer fw.Close() 64 | 65 | h, hok := w.(http.Hijacker) 66 | if !hok { /* w is not Hijacker... oh well... */ 67 | h = nil 68 | } 69 | 70 | w = &compressResponseWriter{ 71 | Writer: fw, 72 | ResponseWriter: w, 73 | Hijacker: h, 74 | } 75 | 76 | break L 77 | } 78 | } 79 | 80 | h.ServeHTTP(w, r) 81 | }) 82 | } 83 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | **/*.[568ao] 3 | **/*.ao 4 | **/*.so 5 | **/*.pyc 6 | **/._* 7 | **/.nfs.* 8 | **/[568a].out 9 | **/*.exe 10 | **/*~ 11 | **/*.orig 12 | **/*.out 13 | **/*.test 14 | core 15 | _obj 16 | _test 17 | out.png 18 | _test* 19 | 20 | **/*.dll 21 | **/core*[0-9] 22 | .private 23 | 24 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/AUTHORS: -------------------------------------------------------------------------------- 1 | Laurent Le Goff 2 | Stani Michiels, gmail:stani.be -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Laurent Le Goff 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 10 | disclaimer in the documentation and/or other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 13 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 14 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 15 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 16 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 17 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 18 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/README.md: -------------------------------------------------------------------------------- 1 | draw2d 2 | ====== 3 | [![Coverage](http://gocover.io/_badge/github.com/llgcode/draw2d?0)](http://gocover.io/github.com/llgcode/draw2d) 4 | [![GoDoc](https://godoc.org/github.com/llgcode/draw2d?status.svg)](https://godoc.org/github.com/llgcode/draw2d) 5 | 6 | Package draw2d is a pure [go](http://golang.org) 2D vector graphics library with support for multiple output devices such as [images](http://golang.org/pkg/image) (draw2d), pdf documents (draw2dpdf) and opengl (draw2dgl), which can also be used on the google app engine. It can be used as a pure go [Cairo](http://www.cairographics.org/) alternative. draw2d is released under the BSD license. See the [documentation](http://godoc.org/github.com/llgcode/draw2d) for more details. 7 | 8 | [![geometry](https://raw.githubusercontent.com/llgcode/draw2d/master/output/samples/geometry.png)](https://raw.githubusercontent.com/llgcode/draw2d/master/resource/image/geometry.pdf)[![postscript](https://raw.githubusercontent.com/llgcode/draw2d/master/output/samples/postscript.png)](https://raw.githubusercontent.com/llgcode/draw2d/master/resource/image/postscript.pdf) 9 | 10 | Click on an image above to get the pdf, generated with exactly the same draw2d code. The first image is the output of `samples/geometry`. The second image is the result of `samples/postcript`, which demonstrates that draw2d can draw postscript files into images or pdf documents with the [ps](https://github.com/llgcode/ps) package. 11 | 12 | Features 13 | -------- 14 | 15 | Operations in draw2d include stroking and filling polygons, arcs, Bézier curves, drawing images and text rendering with truetype fonts. All drawing operations can be transformed by affine transformations (scale, rotation, translation). 16 | 17 | Package draw2d follows the conventions of the [HTML Canvas 2D Context](http://www.w3.org/TR/2dcontext/) for coordinate system, angles, etc... 18 | 19 | Installation 20 | ------------ 21 | 22 | Install [golang](http://golang.org/doc/install). To install or update the package draw2d on your system, run: 23 | 24 | Stable release 25 | ``` 26 | go get -u gopkg.in/llgcode/draw2d.v1 27 | ``` 28 | 29 | or Current release 30 | ``` 31 | go get -u github.com/llgcode/draw2d 32 | ``` 33 | 34 | 35 | Quick Start 36 | ----------- 37 | 38 | The following Go code generates a simple drawing and saves it to an image file with package draw2d: 39 | 40 | ```go 41 | package main 42 | 43 | import ( 44 | "github.com/llgcode/draw2d/draw2dimg" 45 | "image" 46 | "image/color" 47 | ) 48 | 49 | func main() { 50 | // Initialize the graphic context on an RGBA image 51 | dest := image.NewRGBA(image.Rect(0, 0, 297, 210.0)) 52 | gc := draw2dimg.NewGraphicContext(dest) 53 | 54 | // Set some properties 55 | gc.SetFillColor(color.RGBA{0x44, 0xff, 0x44, 0xff}) 56 | gc.SetStrokeColor(color.RGBA{0x44, 0x44, 0x44, 0xff}) 57 | gc.SetLineWidth(5) 58 | 59 | // Draw a closed shape 60 | gc.MoveTo(10, 10) // should always be called first for a new path 61 | gc.LineTo(100, 50) 62 | gc.QuadCurveTo(100, 10, 10, 10) 63 | gc.Close() 64 | gc.FillStroke() 65 | 66 | // Save to file 67 | draw2dimg.SaveToPngFile("hello.png", dest) 68 | } 69 | ``` 70 | 71 | The same Go code can also generate a pdf document with package draw2dpdf: 72 | 73 | ```go 74 | package main 75 | 76 | import ( 77 | "github.com/llgcode/draw2d/draw2dpdf" 78 | "image/color" 79 | ) 80 | 81 | func main() { 82 | // Initialize the graphic context on an RGBA image 83 | dest := draw2dpdf.NewPdf("L", "mm", "A4") 84 | gc := draw2dpdf.NewGraphicContext(dest) 85 | 86 | // Set some properties 87 | gc.SetFillColor(color.RGBA{0x44, 0xff, 0x44, 0xff}) 88 | gc.SetStrokeColor(color.RGBA{0x44, 0x44, 0x44, 0xff}) 89 | gc.SetLineWidth(5) 90 | 91 | // Draw a closed shape 92 | gc.MoveTo(10, 10) // should always be called first for a new path 93 | gc.LineTo(100, 50) 94 | gc.QuadCurveTo(100, 10, 10, 10) 95 | gc.Close() 96 | gc.FillStroke() 97 | 98 | // Save to file 99 | draw2dpdf.SaveToPdfFile("hello.pdf", dest) 100 | } 101 | ``` 102 | 103 | There are more examples here: https://github.com/llgcode/draw2d/tree/master/samples 104 | 105 | Drawing on opengl is provided by the draw2dgl package. 106 | 107 | Testing 108 | ------- 109 | 110 | The samples are run as tests from the root package folder `draw2d` by: 111 | ``` 112 | go test ./... 113 | ``` 114 | Or if you want to run with test coverage: 115 | ``` 116 | go test -cover ./... | grep -v "no test" 117 | ``` 118 | This will generate output by the different backends in the output folder. 119 | 120 | Acknowledgments 121 | --------------- 122 | 123 | [Laurent Le Goff](https://github.com/llgcode) wrote this library, inspired by [Postscript](http://www.tailrecursive.org/postscript) and [HTML5 canvas](http://www.w3.org/TR/2dcontext/). He implemented the image and opengl backend with the [freetype-go](https://code.google.com/p/freetype-go/) package. Also he created a pure go [Postscript interpreter](https://github.com/llgcode/ps), which can read postscript images and draw to a draw2d graphic context. [Stani Michiels](https://github.com/stanim) implemented the pdf backend with the [gofpdf](https://github.com/jung-kurt/gofpdf) package. 124 | 125 | 126 | 127 | Packages using draw2d 128 | --------------------- 129 | 130 | - [ps](https://github.com/llgcode/ps): Postscript interpreter written in Go 131 | - [gonum/plot](https://github.com/gonum/plot): drawing plots in Go 132 | - [go.uik](https://github.com/skelterjohn/go.uik): a concurrent UI kit written in pure go. 133 | - [smartcrop](https://github.com/muesli/smartcrop): content aware image cropping 134 | - [karta](https://github.com/peterhellberg/karta): drawing Voronoi diagrams 135 | - [chart](https://github.com/vdobler/chart): basic charts in Go 136 | - [hilbert](https://github.com/google/hilbert): package for drawing Hilbert curves 137 | 138 | References 139 | --------- 140 | 141 | - [antigrain.com](http://www.antigrain.com) 142 | - [freetype-go](http://code.google.com/p/freetype-go) 143 | - 144 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dbase/README.md: -------------------------------------------------------------------------------- 1 | draw2d/draw2dbase 2 | ================= 3 | 4 | [![Coverage](http://gocover.io/_badge/github.com/llgcode/draw2d/draw2dbase?0)](http://gocover.io/github.com/llgcode/draw2d/draw2dbase) 5 | [![GoDoc](https://godoc.org/github.com/llgcode/draw2d/draw2dbase?status.svg)](https://godoc.org/github.com/llgcode/draw2d/draw2dbase) 6 | 7 | Base package implementation that is used by pdf, svg, img, gl implementations. 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dbase/curve.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The draw2d Authors. All rights reserved. 2 | // created: 17/05/2011 by Laurent Le Goff 3 | 4 | package draw2dbase 5 | 6 | import ( 7 | "math" 8 | ) 9 | 10 | const ( 11 | // CurveRecursionLimit represents the maximum recursion that is really necessary to subsivide a curve into straight lines 12 | CurveRecursionLimit = 32 13 | ) 14 | 15 | // Cubic 16 | // x1, y1, cpx1, cpy1, cpx2, cpy2, x2, y2 float64 17 | 18 | // SubdivideCubic a Bezier cubic curve in 2 equivalents Bezier cubic curves. 19 | // c1 and c2 parameters are the resulting curves 20 | func SubdivideCubic(c, c1, c2 []float64) { 21 | // First point of c is the first point of c1 22 | c1[0], c1[1] = c[0], c[1] 23 | // Last point of c is the last point of c2 24 | c2[6], c2[7] = c[6], c[7] 25 | 26 | // Subdivide segment using midpoints 27 | c1[2] = (c[0] + c[2]) / 2 28 | c1[3] = (c[1] + c[3]) / 2 29 | 30 | midX := (c[2] + c[4]) / 2 31 | midY := (c[3] + c[5]) / 2 32 | 33 | c2[4] = (c[4] + c[6]) / 2 34 | c2[5] = (c[5] + c[7]) / 2 35 | 36 | c1[4] = (c1[2] + midX) / 2 37 | c1[5] = (c1[3] + midY) / 2 38 | 39 | c2[2] = (midX + c2[4]) / 2 40 | c2[3] = (midY + c2[5]) / 2 41 | 42 | c1[6] = (c1[4] + c2[2]) / 2 43 | c1[7] = (c1[5] + c2[3]) / 2 44 | 45 | // Last Point of c1 is equal to the first point of c2 46 | c2[0], c2[1] = c1[6], c1[7] 47 | } 48 | 49 | // TraceCubic generate lines subdividing the cubic curve using a Liner 50 | // flattening_threshold helps determines the flattening expectation of the curve 51 | func TraceCubic(t Liner, cubic []float64, flatteningThreshold float64) { 52 | // Allocation curves 53 | var curves [CurveRecursionLimit * 8]float64 54 | copy(curves[0:8], cubic[0:8]) 55 | i := 0 56 | 57 | // current curve 58 | var c []float64 59 | 60 | var dx, dy, d2, d3 float64 61 | 62 | for i >= 0 { 63 | c = curves[i*8:] 64 | dx = c[6] - c[0] 65 | dy = c[7] - c[1] 66 | 67 | d2 = math.Abs((c[2]-c[6])*dy - (c[3]-c[7])*dx) 68 | d3 = math.Abs((c[4]-c[6])*dy - (c[5]-c[7])*dx) 69 | 70 | // if it's flat then trace a line 71 | if (d2+d3)*(d2+d3) < flatteningThreshold*(dx*dx+dy*dy) || i == len(curves)-1 { 72 | t.LineTo(c[6], c[7]) 73 | i-- 74 | } else { 75 | // second half of bezier go lower onto the stack 76 | SubdivideCubic(c, curves[(i+1)*8:], curves[i*8:]) 77 | i++ 78 | } 79 | } 80 | } 81 | 82 | // Quad 83 | // x1, y1, cpx1, cpy2, x2, y2 float64 84 | 85 | // SubdivideQuad a Bezier quad curve in 2 equivalents Bezier quad curves. 86 | // c1 and c2 parameters are the resulting curves 87 | func SubdivideQuad(c, c1, c2 []float64) { 88 | // First point of c is the first point of c1 89 | c1[0], c1[1] = c[0], c[1] 90 | // Last point of c is the last point of c2 91 | c2[4], c2[5] = c[4], c[5] 92 | 93 | // Subdivide segment using midpoints 94 | c1[2] = (c[0] + c[2]) / 2 95 | c1[3] = (c[1] + c[3]) / 2 96 | c2[2] = (c[2] + c[4]) / 2 97 | c2[3] = (c[3] + c[5]) / 2 98 | c1[4] = (c1[2] + c2[2]) / 2 99 | c1[5] = (c1[3] + c2[3]) / 2 100 | c2[0], c2[1] = c1[4], c1[5] 101 | return 102 | } 103 | 104 | // TraceQuad generate lines subdividing the curve using a Liner 105 | // flattening_threshold helps determines the flattening expectation of the curve 106 | func TraceQuad(t Liner, quad []float64, flatteningThreshold float64) { 107 | // Allocates curves stack 108 | var curves [CurveRecursionLimit * 6]float64 109 | copy(curves[0:6], quad[0:6]) 110 | i := 0 111 | // current curve 112 | var c []float64 113 | var dx, dy, d float64 114 | 115 | for i >= 0 { 116 | c = curves[i*6:] 117 | dx = c[4] - c[0] 118 | dy = c[5] - c[1] 119 | 120 | d = math.Abs(((c[2]-c[4])*dy - (c[3]-c[5])*dx)) 121 | 122 | // if it's flat then trace a line 123 | if (d*d) < flatteningThreshold*(dx*dx+dy*dy) || i == len(curves)-1 { 124 | t.LineTo(c[4], c[5]) 125 | i-- 126 | } else { 127 | // second half of bezier go lower onto the stack 128 | SubdivideQuad(c, curves[(i+1)*6:], curves[i*6:]) 129 | i++ 130 | } 131 | } 132 | } 133 | 134 | // TraceArc trace an arc using a Liner 135 | func TraceArc(t Liner, x, y, rx, ry, start, angle, scale float64) (lastX, lastY float64) { 136 | end := start + angle 137 | clockWise := true 138 | if angle < 0 { 139 | clockWise = false 140 | } 141 | ra := (math.Abs(rx) + math.Abs(ry)) / 2 142 | da := math.Acos(ra/(ra+0.125/scale)) * 2 143 | //normalize 144 | if !clockWise { 145 | da = -da 146 | } 147 | angle = start + da 148 | var curX, curY float64 149 | for { 150 | if (angle < end-da/4) != clockWise { 151 | curX = x + math.Cos(end)*rx 152 | curY = y + math.Sin(end)*ry 153 | return curX, curY 154 | } 155 | curX = x + math.Cos(angle)*rx 156 | curY = y + math.Sin(angle)*ry 157 | 158 | angle += da 159 | t.LineTo(curX, curY) 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dbase/dasher.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The draw2d Authors. All rights reserved. 2 | // created: 13/12/2010 by Laurent Le Goff 3 | 4 | package draw2dbase 5 | 6 | type DashVertexConverter struct { 7 | next Flattener 8 | x, y, distance float64 9 | dash []float64 10 | currentDash int 11 | dashOffset float64 12 | } 13 | 14 | func NewDashConverter(dash []float64, dashOffset float64, flattener Flattener) *DashVertexConverter { 15 | var dasher DashVertexConverter 16 | dasher.dash = dash 17 | dasher.currentDash = 0 18 | dasher.dashOffset = dashOffset 19 | dasher.next = flattener 20 | return &dasher 21 | } 22 | 23 | func (dasher *DashVertexConverter) LineTo(x, y float64) { 24 | dasher.lineTo(x, y) 25 | } 26 | 27 | func (dasher *DashVertexConverter) MoveTo(x, y float64) { 28 | dasher.next.MoveTo(x, y) 29 | dasher.x, dasher.y = x, y 30 | dasher.distance = dasher.dashOffset 31 | dasher.currentDash = 0 32 | } 33 | 34 | func (dasher *DashVertexConverter) LineJoin() { 35 | dasher.next.LineJoin() 36 | } 37 | 38 | func (dasher *DashVertexConverter) Close() { 39 | dasher.next.Close() 40 | } 41 | 42 | func (dasher *DashVertexConverter) End() { 43 | dasher.next.End() 44 | } 45 | 46 | func (dasher *DashVertexConverter) lineTo(x, y float64) { 47 | rest := dasher.dash[dasher.currentDash] - dasher.distance 48 | for rest < 0 { 49 | dasher.distance = dasher.distance - dasher.dash[dasher.currentDash] 50 | dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash) 51 | rest = dasher.dash[dasher.currentDash] - dasher.distance 52 | } 53 | d := distance(dasher.x, dasher.y, x, y) 54 | for d >= rest { 55 | k := rest / d 56 | lx := dasher.x + k*(x-dasher.x) 57 | ly := dasher.y + k*(y-dasher.y) 58 | if dasher.currentDash%2 == 0 { 59 | // line 60 | dasher.next.LineTo(lx, ly) 61 | } else { 62 | // gap 63 | dasher.next.End() 64 | dasher.next.MoveTo(lx, ly) 65 | } 66 | d = d - rest 67 | dasher.x, dasher.y = lx, ly 68 | dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash) 69 | rest = dasher.dash[dasher.currentDash] 70 | } 71 | dasher.distance = d 72 | if dasher.currentDash%2 == 0 { 73 | // line 74 | dasher.next.LineTo(x, y) 75 | } else { 76 | // gap 77 | dasher.next.End() 78 | dasher.next.MoveTo(x, y) 79 | } 80 | if dasher.distance >= dasher.dash[dasher.currentDash] { 81 | dasher.distance = dasher.distance - dasher.dash[dasher.currentDash] 82 | dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash) 83 | } 84 | dasher.x, dasher.y = x, y 85 | } 86 | 87 | func distance(x1, y1, x2, y2 float64) float64 { 88 | return vectorDistance(x2-x1, y2-y1) 89 | } 90 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dbase/demux_flattener.go: -------------------------------------------------------------------------------- 1 | package draw2dbase 2 | 3 | type DemuxFlattener struct { 4 | Flatteners []Flattener 5 | } 6 | 7 | func (dc DemuxFlattener) MoveTo(x, y float64) { 8 | for _, flattener := range dc.Flatteners { 9 | flattener.MoveTo(x, y) 10 | } 11 | } 12 | 13 | func (dc DemuxFlattener) LineTo(x, y float64) { 14 | for _, flattener := range dc.Flatteners { 15 | flattener.LineTo(x, y) 16 | } 17 | } 18 | 19 | func (dc DemuxFlattener) LineJoin() { 20 | for _, flattener := range dc.Flatteners { 21 | flattener.LineJoin() 22 | } 23 | } 24 | 25 | func (dc DemuxFlattener) Close() { 26 | for _, flattener := range dc.Flatteners { 27 | flattener.Close() 28 | } 29 | } 30 | 31 | func (dc DemuxFlattener) End() { 32 | for _, flattener := range dc.Flatteners { 33 | flattener.End() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dbase/flattener.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The draw2d Authors. All rights reserved. 2 | // created: 06/12/2010 by Laurent Le Goff 3 | 4 | package draw2dbase 5 | 6 | import ( 7 | "github.com/kevinburke/rct/Godeps/_workspace/src/github.com/llgcode/draw2d" 8 | ) 9 | 10 | // Liner receive segment definition 11 | type Liner interface { 12 | // LineTo Draw a line from the current position to the point (x, y) 13 | LineTo(x, y float64) 14 | } 15 | 16 | // Flattener receive segment definition 17 | type Flattener interface { 18 | // MoveTo Start a New line from the point (x, y) 19 | MoveTo(x, y float64) 20 | // LineTo Draw a line from the current position to the point (x, y) 21 | LineTo(x, y float64) 22 | // LineJoin add the most recent starting point to close the path to create a polygon 23 | LineJoin() 24 | // Close add the most recent starting point to close the path to create a polygon 25 | Close() 26 | // End mark the current line as finished so we can draw caps 27 | End() 28 | } 29 | 30 | // Flatten convert curves into straight segments keeping join segments info 31 | func Flatten(path *draw2d.Path, flattener Flattener, scale float64) { 32 | // First Point 33 | var startX, startY float64 = 0, 0 34 | // Current Point 35 | var x, y float64 = 0, 0 36 | i := 0 37 | for _, cmp := range path.Components { 38 | switch cmp { 39 | case draw2d.MoveToCmp: 40 | x, y = path.Points[i], path.Points[i+1] 41 | startX, startY = x, y 42 | if i != 0 { 43 | flattener.End() 44 | } 45 | flattener.MoveTo(x, y) 46 | i += 2 47 | case draw2d.LineToCmp: 48 | x, y = path.Points[i], path.Points[i+1] 49 | flattener.LineTo(x, y) 50 | flattener.LineJoin() 51 | i += 2 52 | case draw2d.QuadCurveToCmp: 53 | TraceQuad(flattener, path.Points[i-2:], 0.5) 54 | x, y = path.Points[i+2], path.Points[i+3] 55 | flattener.LineTo(x, y) 56 | i += 4 57 | case draw2d.CubicCurveToCmp: 58 | TraceCubic(flattener, path.Points[i-2:], 0.5) 59 | x, y = path.Points[i+4], path.Points[i+5] 60 | flattener.LineTo(x, y) 61 | i += 6 62 | case draw2d.ArcToCmp: 63 | x, y = TraceArc(flattener, path.Points[i], path.Points[i+1], path.Points[i+2], path.Points[i+3], path.Points[i+4], path.Points[i+5], scale) 64 | flattener.LineTo(x, y) 65 | i += 6 66 | case draw2d.CloseCmp: 67 | flattener.LineTo(startX, startY) 68 | flattener.Close() 69 | } 70 | } 71 | flattener.End() 72 | } 73 | 74 | // Transformer apply the Matrix transformation tr 75 | type Transformer struct { 76 | Tr draw2d.Matrix 77 | Flattener Flattener 78 | } 79 | 80 | func (t Transformer) MoveTo(x, y float64) { 81 | u := x*t.Tr[0] + y*t.Tr[2] + t.Tr[4] 82 | v := x*t.Tr[1] + y*t.Tr[3] + t.Tr[5] 83 | t.Flattener.MoveTo(u, v) 84 | } 85 | 86 | func (t Transformer) LineTo(x, y float64) { 87 | u := x*t.Tr[0] + y*t.Tr[2] + t.Tr[4] 88 | v := x*t.Tr[1] + y*t.Tr[3] + t.Tr[5] 89 | t.Flattener.LineTo(u, v) 90 | } 91 | 92 | func (t Transformer) LineJoin() { 93 | t.Flattener.LineJoin() 94 | } 95 | 96 | func (t Transformer) Close() { 97 | t.Flattener.Close() 98 | } 99 | 100 | func (t Transformer) End() { 101 | t.Flattener.End() 102 | } 103 | 104 | type SegmentedPath struct { 105 | Points []float64 106 | } 107 | 108 | func (p *SegmentedPath) MoveTo(x, y float64) { 109 | p.Points = append(p.Points, x, y) 110 | // TODO need to mark this point as moveto 111 | } 112 | 113 | func (p *SegmentedPath) LineTo(x, y float64) { 114 | p.Points = append(p.Points, x, y) 115 | } 116 | 117 | func (p *SegmentedPath) LineJoin() { 118 | // TODO need to mark the current point as linejoin 119 | } 120 | 121 | func (p *SegmentedPath) Close() { 122 | // TODO Close 123 | } 124 | 125 | func (p *SegmentedPath) End() { 126 | // Nothing to do 127 | } 128 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dbase/line.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The draw2d Authors. All rights reserved. 2 | // created: 27/05/2011 by Laurent Le Goff 3 | 4 | package draw2dbase 5 | 6 | import ( 7 | "image/color" 8 | "image/draw" 9 | ) 10 | 11 | func abs(i int) int { 12 | if i < 0 { 13 | return -i 14 | } 15 | return i 16 | } 17 | 18 | // PolylineBresenham draws a polyline to an image 19 | func PolylineBresenham(img draw.Image, c color.Color, s ...float64) { 20 | for i := 2; i < len(s); i += 2 { 21 | Bresenham(img, c, int(s[i-2]+0.5), int(s[i-1]+0.5), int(s[i]+0.5), int(s[i+1]+0.5)) 22 | } 23 | } 24 | 25 | // Bresenham draws a line between (x0, y0) and (x1, y1) 26 | func Bresenham(img draw.Image, color color.Color, x0, y0, x1, y1 int) { 27 | dx := abs(x1 - x0) 28 | dy := abs(y1 - y0) 29 | var sx, sy int 30 | if x0 < x1 { 31 | sx = 1 32 | } else { 33 | sx = -1 34 | } 35 | if y0 < y1 { 36 | sy = 1 37 | } else { 38 | sy = -1 39 | } 40 | err := dx - dy 41 | 42 | var e2 int 43 | for { 44 | img.Set(x0, y0, color) 45 | if x0 == x1 && y0 == y1 { 46 | return 47 | } 48 | e2 = 2 * err 49 | if e2 > -dy { 50 | err = err - dy 51 | x0 = x0 + sx 52 | } 53 | if e2 < dx { 54 | err = err + dx 55 | y0 = y0 + sy 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dbase/stack_gc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The draw2d Authors. All rights reserved. 2 | // created: 21/11/2010 by Laurent Le Goff 3 | 4 | package draw2dbase 5 | 6 | import ( 7 | "image" 8 | "image/color" 9 | 10 | "github.com/kevinburke/rct/Godeps/_workspace/src/github.com/llgcode/draw2d" 11 | 12 | "github.com/kevinburke/rct/Godeps/_workspace/src/github.com/golang/freetype/truetype" 13 | ) 14 | 15 | var DefaultFontData = draw2d.FontData{Name: "luxi", Family: draw2d.FontFamilySans, Style: draw2d.FontStyleNormal} 16 | 17 | type StackGraphicContext struct { 18 | Current *ContextStack 19 | } 20 | 21 | type ContextStack struct { 22 | Tr draw2d.Matrix 23 | Path *draw2d.Path 24 | LineWidth float64 25 | Dash []float64 26 | DashOffset float64 27 | StrokeColor color.Color 28 | FillColor color.Color 29 | FillRule draw2d.FillRule 30 | Cap draw2d.LineCap 31 | Join draw2d.LineJoin 32 | FontSize float64 33 | FontData draw2d.FontData 34 | 35 | Font *truetype.Font 36 | // fontSize and dpi are used to calculate scale. scale is the number of 37 | // 26.6 fixed point units in 1 em. 38 | Scale float64 39 | 40 | Previous *ContextStack 41 | } 42 | 43 | /** 44 | * Create a new Graphic context from an image 45 | */ 46 | func NewStackGraphicContext() *StackGraphicContext { 47 | gc := &StackGraphicContext{} 48 | gc.Current = new(ContextStack) 49 | gc.Current.Tr = draw2d.NewIdentityMatrix() 50 | gc.Current.Path = new(draw2d.Path) 51 | gc.Current.LineWidth = 1.0 52 | gc.Current.StrokeColor = image.Black 53 | gc.Current.FillColor = image.White 54 | gc.Current.Cap = draw2d.RoundCap 55 | gc.Current.FillRule = draw2d.FillRuleEvenOdd 56 | gc.Current.Join = draw2d.RoundJoin 57 | gc.Current.FontSize = 10 58 | gc.Current.FontData = DefaultFontData 59 | return gc 60 | } 61 | 62 | func (gc *StackGraphicContext) GetMatrixTransform() draw2d.Matrix { 63 | return gc.Current.Tr 64 | } 65 | 66 | func (gc *StackGraphicContext) SetMatrixTransform(Tr draw2d.Matrix) { 67 | gc.Current.Tr = Tr 68 | } 69 | 70 | func (gc *StackGraphicContext) ComposeMatrixTransform(Tr draw2d.Matrix) { 71 | gc.Current.Tr.Compose(Tr) 72 | } 73 | 74 | func (gc *StackGraphicContext) Rotate(angle float64) { 75 | gc.Current.Tr.Rotate(angle) 76 | } 77 | 78 | func (gc *StackGraphicContext) Translate(tx, ty float64) { 79 | gc.Current.Tr.Translate(tx, ty) 80 | } 81 | 82 | func (gc *StackGraphicContext) Scale(sx, sy float64) { 83 | gc.Current.Tr.Scale(sx, sy) 84 | } 85 | 86 | func (gc *StackGraphicContext) SetStrokeColor(c color.Color) { 87 | gc.Current.StrokeColor = c 88 | } 89 | 90 | func (gc *StackGraphicContext) SetFillColor(c color.Color) { 91 | gc.Current.FillColor = c 92 | } 93 | 94 | func (gc *StackGraphicContext) SetFillRule(f draw2d.FillRule) { 95 | gc.Current.FillRule = f 96 | } 97 | 98 | func (gc *StackGraphicContext) SetLineWidth(lineWidth float64) { 99 | gc.Current.LineWidth = lineWidth 100 | } 101 | 102 | func (gc *StackGraphicContext) SetLineCap(cap draw2d.LineCap) { 103 | gc.Current.Cap = cap 104 | } 105 | 106 | func (gc *StackGraphicContext) SetLineJoin(join draw2d.LineJoin) { 107 | gc.Current.Join = join 108 | } 109 | 110 | func (gc *StackGraphicContext) SetLineDash(dash []float64, dashOffset float64) { 111 | gc.Current.Dash = dash 112 | gc.Current.DashOffset = dashOffset 113 | } 114 | 115 | func (gc *StackGraphicContext) SetFontSize(fontSize float64) { 116 | gc.Current.FontSize = fontSize 117 | } 118 | 119 | func (gc *StackGraphicContext) GetFontSize() float64 { 120 | return gc.Current.FontSize 121 | } 122 | 123 | func (gc *StackGraphicContext) SetFontData(fontData draw2d.FontData) { 124 | gc.Current.FontData = fontData 125 | } 126 | 127 | func (gc *StackGraphicContext) GetFontData() draw2d.FontData { 128 | return gc.Current.FontData 129 | } 130 | 131 | func (gc *StackGraphicContext) BeginPath() { 132 | gc.Current.Path.Clear() 133 | } 134 | 135 | func (gc *StackGraphicContext) IsEmpty() bool { 136 | return gc.Current.Path.IsEmpty() 137 | } 138 | 139 | func (gc *StackGraphicContext) LastPoint() (float64, float64) { 140 | return gc.Current.Path.LastPoint() 141 | } 142 | 143 | func (gc *StackGraphicContext) MoveTo(x, y float64) { 144 | gc.Current.Path.MoveTo(x, y) 145 | } 146 | 147 | func (gc *StackGraphicContext) LineTo(x, y float64) { 148 | gc.Current.Path.LineTo(x, y) 149 | } 150 | 151 | func (gc *StackGraphicContext) QuadCurveTo(cx, cy, x, y float64) { 152 | gc.Current.Path.QuadCurveTo(cx, cy, x, y) 153 | } 154 | 155 | func (gc *StackGraphicContext) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) { 156 | gc.Current.Path.CubicCurveTo(cx1, cy1, cx2, cy2, x, y) 157 | } 158 | 159 | func (gc *StackGraphicContext) ArcTo(cx, cy, rx, ry, startAngle, angle float64) { 160 | gc.Current.Path.ArcTo(cx, cy, rx, ry, startAngle, angle) 161 | } 162 | 163 | func (gc *StackGraphicContext) Close() { 164 | gc.Current.Path.Close() 165 | } 166 | 167 | func (gc *StackGraphicContext) Save() { 168 | context := new(ContextStack) 169 | context.FontSize = gc.Current.FontSize 170 | context.FontData = gc.Current.FontData 171 | context.LineWidth = gc.Current.LineWidth 172 | context.StrokeColor = gc.Current.StrokeColor 173 | context.FillColor = gc.Current.FillColor 174 | context.FillRule = gc.Current.FillRule 175 | context.Dash = gc.Current.Dash 176 | context.DashOffset = gc.Current.DashOffset 177 | context.Cap = gc.Current.Cap 178 | context.Join = gc.Current.Join 179 | context.Path = gc.Current.Path.Copy() 180 | context.Font = gc.Current.Font 181 | context.Scale = gc.Current.Scale 182 | copy(context.Tr[:], gc.Current.Tr[:]) 183 | context.Previous = gc.Current 184 | gc.Current = context 185 | } 186 | 187 | func (gc *StackGraphicContext) Restore() { 188 | if gc.Current.Previous != nil { 189 | oldContext := gc.Current 190 | gc.Current = gc.Current.Previous 191 | oldContext.Previous = nil 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dbase/stroker.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The draw2d Authors. All rights reserved. 2 | // created: 13/12/2010 by Laurent Le Goff 3 | 4 | package draw2dbase 5 | 6 | import ( 7 | "math" 8 | 9 | "github.com/kevinburke/rct/Godeps/_workspace/src/github.com/llgcode/draw2d" 10 | ) 11 | 12 | type LineStroker struct { 13 | Flattener Flattener 14 | HalfLineWidth float64 15 | Cap draw2d.LineCap 16 | Join draw2d.LineJoin 17 | vertices []float64 18 | rewind []float64 19 | x, y, nx, ny float64 20 | } 21 | 22 | func NewLineStroker(c draw2d.LineCap, j draw2d.LineJoin, flattener Flattener) *LineStroker { 23 | l := new(LineStroker) 24 | l.Flattener = flattener 25 | l.HalfLineWidth = 0.5 26 | l.Cap = c 27 | l.Join = j 28 | return l 29 | } 30 | 31 | func (l *LineStroker) MoveTo(x, y float64) { 32 | l.x, l.y = x, y 33 | } 34 | 35 | func (l *LineStroker) LineTo(x, y float64) { 36 | l.line(l.x, l.y, x, y) 37 | } 38 | 39 | func (l *LineStroker) LineJoin() { 40 | 41 | } 42 | 43 | func (l *LineStroker) line(x1, y1, x2, y2 float64) { 44 | dx := (x2 - x1) 45 | dy := (y2 - y1) 46 | d := vectorDistance(dx, dy) 47 | if d != 0 { 48 | nx := dy * l.HalfLineWidth / d 49 | ny := -(dx * l.HalfLineWidth / d) 50 | l.appendVertex(x1+nx, y1+ny, x2+nx, y2+ny, x1-nx, y1-ny, x2-nx, y2-ny) 51 | l.x, l.y, l.nx, l.ny = x2, y2, nx, ny 52 | } 53 | } 54 | 55 | func (l *LineStroker) Close() { 56 | if len(l.vertices) > 1 { 57 | l.appendVertex(l.vertices[0], l.vertices[1], l.rewind[0], l.rewind[1]) 58 | } 59 | } 60 | 61 | func (l *LineStroker) End() { 62 | if len(l.vertices) > 1 { 63 | l.Flattener.MoveTo(l.vertices[0], l.vertices[1]) 64 | for i, j := 2, 3; j < len(l.vertices); i, j = i+2, j+2 { 65 | l.Flattener.LineTo(l.vertices[i], l.vertices[j]) 66 | } 67 | } 68 | for i, j := len(l.rewind)-2, len(l.rewind)-1; j > 0; i, j = i-2, j-2 { 69 | l.Flattener.LineTo(l.rewind[i], l.rewind[j]) 70 | } 71 | if len(l.vertices) > 1 { 72 | l.Flattener.LineTo(l.vertices[0], l.vertices[1]) 73 | } 74 | l.Flattener.End() 75 | // reinit vertices 76 | l.vertices = l.vertices[0:0] 77 | l.rewind = l.rewind[0:0] 78 | l.x, l.y, l.nx, l.ny = 0, 0, 0, 0 79 | 80 | } 81 | 82 | func (l *LineStroker) appendVertex(vertices ...float64) { 83 | s := len(vertices) / 2 84 | l.vertices = append(l.vertices, vertices[:s]...) 85 | l.rewind = append(l.rewind, vertices[s:]...) 86 | } 87 | 88 | func vectorDistance(dx, dy float64) float64 { 89 | return float64(math.Sqrt(dx*dx + dy*dy)) 90 | } 91 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dimg/README.md: -------------------------------------------------------------------------------- 1 | draw2d/draw2dimg 2 | ================= 3 | 4 | [![Coverage](http://gocover.io/_badge/github.com/llgcode/draw2d/draw2dimg?0)](http://gocover.io/github.com/llgcode/draw2d/draw2dimg) 5 | [![GoDoc](https://godoc.org/github.com/llgcode/draw2d/draw2dimg?status.svg)](https://godoc.org/github.com/llgcode/draw2d/draw2dimg) 6 | 7 | 8 | draw2d implementation that generates raster images using https://github.com/golang/freetype package. 9 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dimg/fileutil.go: -------------------------------------------------------------------------------- 1 | package draw2dimg 2 | 3 | import ( 4 | "bufio" 5 | "image" 6 | "image/png" 7 | "os" 8 | ) 9 | 10 | // SaveToPngFile create and save an image to a file using PNG format 11 | func SaveToPngFile(filePath string, m image.Image) error { 12 | // Create the file 13 | f, err := os.Create(filePath) 14 | if err != nil { 15 | return err 16 | } 17 | defer f.Close() 18 | // Create Writer from file 19 | b := bufio.NewWriter(f) 20 | // Write the image into the buffer 21 | err = png.Encode(b, m) 22 | if err != nil { 23 | return err 24 | } 25 | err = b.Flush() 26 | if err != nil { 27 | return err 28 | } 29 | return nil 30 | } 31 | 32 | // LoadFromPngFile Open a png file 33 | func LoadFromPngFile(filePath string) (image.Image, error) { 34 | // Open file 35 | f, err := os.OpenFile(filePath, 0, 0) 36 | if err != nil { 37 | return nil, err 38 | } 39 | defer f.Close() 40 | b := bufio.NewReader(f) 41 | img, err := png.Decode(b) 42 | if err != nil { 43 | return nil, err 44 | } 45 | return img, nil 46 | } 47 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dimg/ftpath.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The draw2d Authors. All rights reserved. 2 | // created: 13/12/2010 by Laurent Le Goff 3 | 4 | package draw2dimg 5 | 6 | import ( 7 | "github.com/kevinburke/rct/Godeps/_workspace/src/github.com/golang/freetype/raster" 8 | "github.com/kevinburke/rct/Godeps/_workspace/src/golang.org/x/image/math/fixed" 9 | ) 10 | 11 | type FtLineBuilder struct { 12 | Adder raster.Adder 13 | } 14 | 15 | func (liner FtLineBuilder) MoveTo(x, y float64) { 16 | liner.Adder.Start(fixed.Point26_6{X: fixed.Int26_6(x * 64), Y: fixed.Int26_6(y * 64)}) 17 | } 18 | 19 | func (liner FtLineBuilder) LineTo(x, y float64) { 20 | liner.Adder.Add1(fixed.Point26_6{X: fixed.Int26_6(x * 64), Y: fixed.Int26_6(y * 64)}) 21 | } 22 | 23 | func (liner FtLineBuilder) LineJoin() { 24 | } 25 | 26 | func (liner FtLineBuilder) Close() { 27 | } 28 | 29 | func (liner FtLineBuilder) End() { 30 | } 31 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dimg/rgba_interpolation.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The draw2d Authors. All rights reserved. 2 | // created: 21/11/2010 by Laurent Le Goff 3 | // see http://pippin.gimp.org/image_processing/chap_resampling.html 4 | 5 | package draw2dimg 6 | 7 | import ( 8 | "image" 9 | "image/color" 10 | "image/draw" 11 | "math" 12 | 13 | "github.com/kevinburke/rct/Godeps/_workspace/src/github.com/llgcode/draw2d" 14 | ) 15 | 16 | // ImageFilter defines the type of filter to use 17 | type ImageFilter int 18 | 19 | const ( 20 | // LinearFilter defines a linear filter 21 | LinearFilter ImageFilter = iota 22 | // BilinearFilter defines a bilinear filter 23 | BilinearFilter 24 | // BicubicFilter defines a bicubic filter 25 | BicubicFilter 26 | // M is the maximum value for a rgb component 27 | M = 1<<16 - 1 28 | ) 29 | 30 | //see http://pippin.gimp.org/image_processing/chap_resampling.html 31 | func getColorLinear(img image.Image, x, y float64) color.Color { 32 | return img.At(int(x), int(y)) 33 | } 34 | 35 | func getColorBilinear(img image.Image, x, y float64) color.Color { 36 | x0 := math.Floor(x) 37 | y0 := math.Floor(y) 38 | dx := x - x0 39 | dy := y - y0 40 | 41 | rt, gt, bt, at := img.At(int(x0), int(y0)).RGBA() 42 | r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at) 43 | rt, gt, bt, at = img.At(int(x0+1), int(y0)).RGBA() 44 | r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at) 45 | rt, gt, bt, at = img.At(int(x0+1), int(y0+1)).RGBA() 46 | r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at) 47 | rt, gt, bt, at = img.At(int(x0), int(y0+1)).RGBA() 48 | r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at) 49 | 50 | r := int(lerp(lerp(r0, r1, dx), lerp(r3, r2, dx), dy)) 51 | g := int(lerp(lerp(g0, g1, dx), lerp(g3, g2, dx), dy)) 52 | b := int(lerp(lerp(b0, b1, dx), lerp(b3, b2, dx), dy)) 53 | a := int(lerp(lerp(a0, a1, dx), lerp(a3, a2, dx), dy)) 54 | return color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} 55 | } 56 | 57 | // lerp is a linear interpolation bertween 2 points 58 | func lerp(v1, v2, ratio float64) float64 { 59 | return v1*(1-ratio) + v2*ratio 60 | } 61 | 62 | func getColorCubicRow(img image.Image, x, y, offset float64) color.Color { 63 | c0 := img.At(int(x), int(y)) 64 | c1 := img.At(int(x+1), int(y)) 65 | c2 := img.At(int(x+2), int(y)) 66 | c3 := img.At(int(x+3), int(y)) 67 | rt, gt, bt, at := c0.RGBA() 68 | r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at) 69 | rt, gt, bt, at = c1.RGBA() 70 | r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at) 71 | rt, gt, bt, at = c2.RGBA() 72 | r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at) 73 | rt, gt, bt, at = c3.RGBA() 74 | r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at) 75 | r, g, b, a := cubic(offset, r0, r1, r2, r3), cubic(offset, g0, g1, g2, g3), cubic(offset, b0, b1, b2, b3), cubic(offset, a0, a1, a2, a3) 76 | return color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} 77 | } 78 | 79 | func getColorBicubic(img image.Image, x, y float64) color.Color { 80 | x0 := math.Floor(x) 81 | y0 := math.Floor(y) 82 | dx := x - x0 83 | dy := y - y0 84 | c0 := getColorCubicRow(img, x0-1, y0-1, dx) 85 | c1 := getColorCubicRow(img, x0-1, y0, dx) 86 | c2 := getColorCubicRow(img, x0-1, y0+1, dx) 87 | c3 := getColorCubicRow(img, x0-1, y0+2, dx) 88 | rt, gt, bt, at := c0.RGBA() 89 | r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at) 90 | rt, gt, bt, at = c1.RGBA() 91 | r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at) 92 | rt, gt, bt, at = c2.RGBA() 93 | r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at) 94 | rt, gt, bt, at = c3.RGBA() 95 | r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at) 96 | r, g, b, a := cubic(dy, r0, r1, r2, r3), cubic(dy, g0, g1, g2, g3), cubic(dy, b0, b1, b2, b3), cubic(dy, a0, a1, a2, a3) 97 | return color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)} 98 | } 99 | 100 | func cubic(offset, v0, v1, v2, v3 float64) uint32 { 101 | // offset is the offset of the sampled value between v1 and v2 102 | return uint32(((((-7*v0+21*v1-21*v2+7*v3)*offset+ 103 | (15*v0-36*v1+27*v2-6*v3))*offset+ 104 | (-9*v0+9*v2))*offset + (v0 + 16*v1 + v2)) / 18.0) 105 | } 106 | 107 | // DrawImage draws an image into dest using an affine transformation matrix, an op and a filter 108 | func DrawImage(src image.Image, dest draw.Image, tr draw2d.Matrix, op draw.Op, filter ImageFilter) { 109 | bounds := src.Bounds() 110 | x0, y0, x1, y1 := tr.TransformRectangle(float64(bounds.Min.X), float64(bounds.Min.Y), float64(bounds.Max.X), float64(bounds.Max.Y)) 111 | var x, y, u, v float64 112 | var c1, c2, cr color.Color 113 | var r, g, b, a, ia, r1, g1, b1, a1, r2, g2, b2, a2 uint32 114 | var color color.RGBA 115 | for x = x0; x < x1; x++ { 116 | for y = y0; y < y1; y++ { 117 | u = x 118 | v = y 119 | u, v = tr.InverseTransformPoint(u, v) 120 | if bounds.Min.X <= int(u) && bounds.Max.X > int(u) && bounds.Min.Y <= int(v) && bounds.Max.Y > int(v) { 121 | c1 = dest.At(int(x), int(y)) 122 | switch filter { 123 | case LinearFilter: 124 | c2 = src.At(int(u), int(v)) 125 | case BilinearFilter: 126 | c2 = getColorBilinear(src, u, v) 127 | case BicubicFilter: 128 | c2 = getColorBicubic(src, u, v) 129 | } 130 | switch op { 131 | case draw.Over: 132 | r1, g1, b1, a1 = c1.RGBA() 133 | r2, g2, b2, a2 = c2.RGBA() 134 | ia = M - a2 135 | r = ((r1 * ia) / M) + r2 136 | g = ((g1 * ia) / M) + g2 137 | b = ((b1 * ia) / M) + b2 138 | a = ((a1 * ia) / M) + a2 139 | color.R = uint8(r >> 8) 140 | color.G = uint8(g >> 8) 141 | color.B = uint8(b >> 8) 142 | color.A = uint8(a >> 8) 143 | cr = color 144 | default: 145 | cr = c2 146 | } 147 | dest.Set(int(x), int(y), cr) 148 | } 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dimg/text.go: -------------------------------------------------------------------------------- 1 | package draw2dimg 2 | 3 | import ( 4 | "github.com/kevinburke/rct/Godeps/_workspace/src/github.com/golang/freetype/truetype" 5 | "github.com/kevinburke/rct/Godeps/_workspace/src/github.com/llgcode/draw2d" 6 | 7 | "github.com/kevinburke/rct/Godeps/_workspace/src/golang.org/x/image/math/fixed" 8 | ) 9 | 10 | // DrawContour draws the given closed contour at the given sub-pixel offset. 11 | func DrawContour(path draw2d.PathBuilder, ps []truetype.Point, dx, dy float64) { 12 | if len(ps) == 0 { 13 | return 14 | } 15 | startX, startY := pointToF64Point(ps[0]) 16 | path.MoveTo(startX+dx, startY+dy) 17 | q0X, q0Y, on0 := startX, startY, true 18 | for _, p := range ps[1:] { 19 | qX, qY := pointToF64Point(p) 20 | on := p.Flags&0x01 != 0 21 | if on { 22 | if on0 { 23 | path.LineTo(qX+dx, qY+dy) 24 | } else { 25 | path.QuadCurveTo(q0X+dx, q0Y+dy, qX+dx, qY+dy) 26 | } 27 | } else { 28 | if on0 { 29 | // No-op. 30 | } else { 31 | midX := (q0X + qX) / 2 32 | midY := (q0Y + qY) / 2 33 | path.QuadCurveTo(q0X+dx, q0Y+dy, midX+dx, midY+dy) 34 | } 35 | } 36 | q0X, q0Y, on0 = qX, qY, on 37 | } 38 | // Close the curve. 39 | if on0 { 40 | path.LineTo(startX+dx, startY+dy) 41 | } else { 42 | path.QuadCurveTo(q0X+dx, q0Y+dy, startX+dx, startY+dy) 43 | } 44 | } 45 | 46 | func pointToF64Point(p truetype.Point) (x, y float64) { 47 | return fUnitsToFloat64(p.X), -fUnitsToFloat64(p.Y) 48 | } 49 | 50 | func fUnitsToFloat64(x fixed.Int26_6) float64 { 51 | scaled := x << 2 52 | return float64(scaled/256) + float64(scaled%256)/256.0 53 | } 54 | 55 | // FontExtents contains font metric information. 56 | type FontExtents struct { 57 | // Ascent is the distance that the text 58 | // extends above the baseline. 59 | Ascent float64 60 | 61 | // Descent is the distance that the text 62 | // extends below the baseline. The descent 63 | // is given as a negative value. 64 | Descent float64 65 | 66 | // Height is the distance from the lowest 67 | // descending point to the highest ascending 68 | // point. 69 | Height float64 70 | } 71 | 72 | // Extents returns the FontExtents for a font. 73 | // TODO needs to read this https://developer.apple.com/fonts/TrueType-Reference-Manual/RM02/Chap2.html#intro 74 | func Extents(font *truetype.Font, size float64) FontExtents { 75 | bounds := font.Bounds(fixed.Int26_6(font.FUnitsPerEm())) 76 | scale := size / float64(font.FUnitsPerEm()) 77 | return FontExtents{ 78 | Ascent: float64(bounds.Max.Y) * scale, 79 | Descent: float64(bounds.Min.Y) * scale, 80 | Height: float64(bounds.Max.Y-bounds.Min.Y) * scale, 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/font.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The draw2d Authors. All rights reserved. 2 | // created: 13/12/2010 by Laurent Le Goff 3 | 4 | package draw2d 5 | 6 | import ( 7 | "io/ioutil" 8 | "log" 9 | "path" 10 | "path/filepath" 11 | 12 | "github.com/kevinburke/rct/Godeps/_workspace/src/github.com/golang/freetype/truetype" 13 | ) 14 | 15 | var ( 16 | fontFolder = "../resource/font/" 17 | fonts = make(map[string]*truetype.Font) 18 | fontNamer FontFileNamer = FontFileName 19 | ) 20 | 21 | // FontStyle defines bold and italic styles for the font 22 | // It is possible to combine values for mixed styles, eg. 23 | // FontData.Style = FontStyleBold | FontStyleItalic 24 | type FontStyle byte 25 | 26 | const ( 27 | FontStyleNormal FontStyle = iota 28 | FontStyleBold 29 | FontStyleItalic 30 | ) 31 | 32 | type FontFamily byte 33 | 34 | const ( 35 | FontFamilySans FontFamily = iota 36 | FontFamilySerif 37 | FontFamilyMono 38 | ) 39 | 40 | type FontData struct { 41 | Name string 42 | Family FontFamily 43 | Style FontStyle 44 | } 45 | 46 | type FontFileNamer func(fontData FontData) string 47 | 48 | func FontFileName(fontData FontData) string { 49 | fontFileName := fontData.Name 50 | switch fontData.Family { 51 | case FontFamilySans: 52 | fontFileName += "s" 53 | case FontFamilySerif: 54 | fontFileName += "r" 55 | case FontFamilyMono: 56 | fontFileName += "m" 57 | } 58 | if fontData.Style&FontStyleBold != 0 { 59 | fontFileName += "b" 60 | } else { 61 | fontFileName += "r" 62 | } 63 | 64 | if fontData.Style&FontStyleItalic != 0 { 65 | fontFileName += "i" 66 | } 67 | fontFileName += ".ttf" 68 | return fontFileName 69 | } 70 | 71 | func RegisterFont(fontData FontData, font *truetype.Font) { 72 | fonts[fontNamer(fontData)] = font 73 | } 74 | 75 | func GetFont(fontData FontData) *truetype.Font { 76 | fontFileName := fontNamer(fontData) 77 | font := fonts[fontFileName] 78 | if font != nil { 79 | return font 80 | } 81 | fonts[fontFileName] = loadFont(fontFileName) 82 | return fonts[fontFileName] 83 | } 84 | 85 | func GetFontFolder() string { 86 | return fontFolder 87 | } 88 | 89 | func SetFontFolder(folder string) { 90 | fontFolder = filepath.Clean(folder) 91 | } 92 | 93 | func SetFontNamer(fn FontFileNamer) { 94 | fontNamer = fn 95 | } 96 | 97 | func loadFont(fontFileName string) *truetype.Font { 98 | fontBytes, err := ioutil.ReadFile(path.Join(fontFolder, fontFileName)) 99 | if err != nil { 100 | log.Println(err) 101 | return nil 102 | } 103 | font, err := truetype.Parse(fontBytes) 104 | if err != nil { 105 | log.Println(err) 106 | return nil 107 | } 108 | return font 109 | } 110 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/gc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The draw2d Authors. All rights reserved. 2 | // created: 21/11/2010 by Laurent Le Goff 3 | 4 | package draw2d 5 | 6 | import ( 7 | "image" 8 | "image/color" 9 | ) 10 | 11 | // GraphicContext describes the interface for the various backends (images, pdf, opengl, ...) 12 | type GraphicContext interface { 13 | PathBuilder 14 | // BeginPath creates a new path 15 | BeginPath() 16 | // GetMatrixTransform returns the current transformation matrix 17 | GetMatrixTransform() Matrix 18 | // SetMatrixTransform sets the current transformation matrix 19 | SetMatrixTransform(tr Matrix) 20 | // ComposeMatrixTransform composes the current transformation matrix with tr 21 | ComposeMatrixTransform(tr Matrix) 22 | // Rotate applies a rotation to the current transformation matrix. angle is in radian. 23 | Rotate(angle float64) 24 | // Translate applies a translation to the current transformation matrix. 25 | Translate(tx, ty float64) 26 | // Scale applies a scale to the current transformation matrix. 27 | Scale(sx, sy float64) 28 | // SetStrokeColor sets the current stroke color 29 | SetStrokeColor(c color.Color) 30 | // SetStrokeColor sets the current fill color 31 | SetFillColor(c color.Color) 32 | // SetFillRule sets the current fill rule 33 | SetFillRule(f FillRule) 34 | // SetLineWidth sets the current line width 35 | SetLineWidth(lineWidth float64) 36 | // SetLineCap sets the current line cap 37 | SetLineCap(cap LineCap) 38 | // SetLineJoin sets the current line join 39 | SetLineJoin(join LineJoin) 40 | // SetLineJoin sets the current dash 41 | SetLineDash(dash []float64, dashOffset float64) 42 | // SetFontSize 43 | SetFontSize(fontSize float64) 44 | GetFontSize() float64 45 | SetFontData(fontData FontData) 46 | GetFontData() FontData 47 | DrawImage(image image.Image) 48 | Save() 49 | Restore() 50 | Clear() 51 | ClearRect(x1, y1, x2, y2 int) 52 | SetDPI(dpi int) 53 | GetDPI() int 54 | GetStringBounds(s string) (left, top, right, bottom float64) 55 | CreateStringPath(text string, x, y float64) (cursor float64) 56 | FillString(text string) (cursor float64) 57 | FillStringAt(text string, x, y float64) (cursor float64) 58 | StrokeString(text string) (cursor float64) 59 | StrokeStringAt(text string, x, y float64) (cursor float64) 60 | Stroke(paths ...*Path) 61 | Fill(paths ...*Path) 62 | FillStroke(paths ...*Path) 63 | } 64 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/path.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The draw2d Authors. All rights reserved. 2 | // created: 21/11/2010 by Laurent Le Goff 3 | 4 | package draw2d 5 | 6 | import ( 7 | "fmt" 8 | "math" 9 | ) 10 | 11 | // PathBuilder describes the interface for path drawing. 12 | type PathBuilder interface { 13 | // LastPoint returns the current point of the current sub path 14 | LastPoint() (x, y float64) 15 | // MoveTo creates a new subpath that start at the specified point 16 | MoveTo(x, y float64) 17 | // LineTo adds a line to the current subpath 18 | LineTo(x, y float64) 19 | // QuadCurveTo adds a quadratic Bézier curve to the current subpath 20 | QuadCurveTo(cx, cy, x, y float64) 21 | // CubicCurveTo adds a cubic Bézier curve to the current subpath 22 | CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) 23 | // ArcTo adds an arc to the current subpath 24 | ArcTo(cx, cy, rx, ry, startAngle, angle float64) 25 | // Close creates a line from the current point to the last MoveTo 26 | // point (if not the same) and mark the path as closed so the 27 | // first and last lines join nicely. 28 | Close() 29 | } 30 | 31 | // PathCmp represents component of a path 32 | type PathCmp int 33 | 34 | const ( 35 | // MoveToCmp is a MoveTo component in a Path 36 | MoveToCmp PathCmp = iota 37 | // LineToCmp is a LineTo component in a Path 38 | LineToCmp 39 | // QuadCurveToCmp is a QuadCurveTo component in a Path 40 | QuadCurveToCmp 41 | // CubicCurveToCmp is a CubicCurveTo component in a Path 42 | CubicCurveToCmp 43 | // ArcToCmp is a ArcTo component in a Path 44 | ArcToCmp 45 | // CloseCmp is a ArcTo component in a Path 46 | CloseCmp 47 | ) 48 | 49 | // Path stores points 50 | type Path struct { 51 | // Components is a slice of PathCmp in a Path and mark the role of each points in the Path 52 | Components []PathCmp 53 | // Points are combined with Components to have a specific role in the path 54 | Points []float64 55 | // Last Point of the Path 56 | x, y float64 57 | } 58 | 59 | func (p *Path) appendToPath(cmd PathCmp, points ...float64) { 60 | p.Components = append(p.Components, cmd) 61 | p.Points = append(p.Points, points...) 62 | } 63 | 64 | // LastPoint returns the current point of the current path 65 | func (p *Path) LastPoint() (x, y float64) { 66 | return p.x, p.y 67 | } 68 | 69 | // MoveTo starts a new path at (x, y) position 70 | func (p *Path) MoveTo(x, y float64) { 71 | p.appendToPath(MoveToCmp, x, y) 72 | p.x = x 73 | p.y = y 74 | } 75 | 76 | // LineTo adds a line to the current path 77 | func (p *Path) LineTo(x, y float64) { 78 | if len(p.Components) == 0 { //special case when no move has been done 79 | p.MoveTo(0, 0) 80 | } 81 | p.appendToPath(LineToCmp, x, y) 82 | p.x = x 83 | p.y = y 84 | } 85 | 86 | // QuadCurveTo adds a quadratic bezier curve to the current path 87 | func (p *Path) QuadCurveTo(cx, cy, x, y float64) { 88 | if len(p.Components) == 0 { //special case when no move has been done 89 | p.MoveTo(0, 0) 90 | } 91 | p.appendToPath(QuadCurveToCmp, cx, cy, x, y) 92 | p.x = x 93 | p.y = y 94 | } 95 | 96 | // CubicCurveTo adds a cubic bezier curve to the current path 97 | func (p *Path) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) { 98 | if len(p.Components) == 0 { //special case when no move has been done 99 | p.MoveTo(0, 0) 100 | } 101 | p.appendToPath(CubicCurveToCmp, cx1, cy1, cx2, cy2, x, y) 102 | p.x = x 103 | p.y = y 104 | } 105 | 106 | // ArcTo adds an arc to the path 107 | func (p *Path) ArcTo(cx, cy, rx, ry, startAngle, angle float64) { 108 | endAngle := startAngle + angle 109 | clockWise := true 110 | if angle < 0 { 111 | clockWise = false 112 | } 113 | // normalize 114 | if clockWise { 115 | for endAngle < startAngle { 116 | endAngle += math.Pi * 2.0 117 | } 118 | } else { 119 | for startAngle < endAngle { 120 | startAngle += math.Pi * 2.0 121 | } 122 | } 123 | startX := cx + math.Cos(startAngle)*rx 124 | startY := cy + math.Sin(startAngle)*ry 125 | if len(p.Components) > 0 { 126 | p.LineTo(startX, startY) 127 | } else { 128 | p.MoveTo(startX, startY) 129 | } 130 | p.appendToPath(ArcToCmp, cx, cy, rx, ry, startAngle, angle) 131 | p.x = cx + math.Cos(endAngle)*rx 132 | p.y = cy + math.Sin(endAngle)*ry 133 | } 134 | 135 | // Close closes the current path 136 | func (p *Path) Close() { 137 | p.appendToPath(CloseCmp) 138 | } 139 | 140 | // Copy make a clone of the current path and return it 141 | func (p *Path) Copy() (dest *Path) { 142 | dest = new(Path) 143 | dest.Components = make([]PathCmp, len(p.Components)) 144 | copy(dest.Components, p.Components) 145 | dest.Points = make([]float64, len(p.Points)) 146 | copy(dest.Points, p.Points) 147 | dest.x, dest.y = p.x, p.y 148 | return dest 149 | } 150 | 151 | // Clear reset the path 152 | func (p *Path) Clear() { 153 | p.Components = p.Components[0:0] 154 | p.Points = p.Points[0:0] 155 | return 156 | } 157 | 158 | // IsEmpty returns true if the path is empty 159 | func (p *Path) IsEmpty() bool { 160 | return len(p.Components) == 0 161 | } 162 | 163 | // String returns a debug text view of the path 164 | func (p *Path) String() string { 165 | s := "" 166 | j := 0 167 | for _, cmd := range p.Components { 168 | switch cmd { 169 | case MoveToCmp: 170 | s += fmt.Sprintf("MoveTo: %f, %f\n", p.Points[j], p.Points[j+1]) 171 | j = j + 2 172 | case LineToCmp: 173 | s += fmt.Sprintf("LineTo: %f, %f\n", p.Points[j], p.Points[j+1]) 174 | j = j + 2 175 | case QuadCurveToCmp: 176 | s += fmt.Sprintf("QuadCurveTo: %f, %f, %f, %f\n", p.Points[j], p.Points[j+1], p.Points[j+2], p.Points[j+3]) 177 | j = j + 4 178 | case CubicCurveToCmp: 179 | s += fmt.Sprintf("CubicCurveTo: %f, %f, %f, %f, %f, %f\n", p.Points[j], p.Points[j+1], p.Points[j+2], p.Points[j+3], p.Points[j+4], p.Points[j+5]) 180 | j = j + 6 181 | case ArcToCmp: 182 | s += fmt.Sprintf("ArcTo: %f, %f, %f, %f, %f, %f\n", p.Points[j], p.Points[j+1], p.Points[j+2], p.Points[j+3], p.Points[j+4], p.Points[j+5]) 183 | j = j + 6 184 | case CloseCmp: 185 | s += "Close\n" 186 | } 187 | } 188 | return s 189 | } 190 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/llgcode/draw2d/test: -------------------------------------------------------------------------------- 1 | echo golint 2 | golint ./... | grep "draw2dpdf\|samples\|^advanced_path\|^arc\|draw2d[.]\|fileutil\|^gc\|math\|^path[.]\|rgba_interpolation\|test\|vertex2d" 3 | echo 4 | echo go vet 5 | go vet ./... 6 | echo 7 | echo go test 8 | go test -cover ./... | grep -v "no test" -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/pborman/uuid/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Paul Borman 2 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/pborman/uuid/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009,2014 Google Inc. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/pborman/uuid/dce.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import ( 8 | "encoding/binary" 9 | "fmt" 10 | "os" 11 | ) 12 | 13 | // A Domain represents a Version 2 domain 14 | type Domain byte 15 | 16 | // Domain constants for DCE Security (Version 2) UUIDs. 17 | const ( 18 | Person = Domain(0) 19 | Group = Domain(1) 20 | Org = Domain(2) 21 | ) 22 | 23 | // NewDCESecurity returns a DCE Security (Version 2) UUID. 24 | // 25 | // The domain should be one of Person, Group or Org. 26 | // On a POSIX system the id should be the users UID for the Person 27 | // domain and the users GID for the Group. The meaning of id for 28 | // the domain Org or on non-POSIX systems is site defined. 29 | // 30 | // For a given domain/id pair the same token may be returned for up to 31 | // 7 minutes and 10 seconds. 32 | func NewDCESecurity(domain Domain, id uint32) UUID { 33 | uuid := NewUUID() 34 | if uuid != nil { 35 | uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 36 | uuid[9] = byte(domain) 37 | binary.BigEndian.PutUint32(uuid[0:], id) 38 | } 39 | return uuid 40 | } 41 | 42 | // NewDCEPerson returns a DCE Security (Version 2) UUID in the person 43 | // domain with the id returned by os.Getuid. 44 | // 45 | // NewDCEPerson(Person, uint32(os.Getuid())) 46 | func NewDCEPerson() UUID { 47 | return NewDCESecurity(Person, uint32(os.Getuid())) 48 | } 49 | 50 | // NewDCEGroup returns a DCE Security (Version 2) UUID in the group 51 | // domain with the id returned by os.Getgid. 52 | // 53 | // NewDCEGroup(Group, uint32(os.Getgid())) 54 | func NewDCEGroup() UUID { 55 | return NewDCESecurity(Group, uint32(os.Getgid())) 56 | } 57 | 58 | // Domain returns the domain for a Version 2 UUID or false. 59 | func (uuid UUID) Domain() (Domain, bool) { 60 | if v, _ := uuid.Version(); v != 2 { 61 | return 0, false 62 | } 63 | return Domain(uuid[9]), true 64 | } 65 | 66 | // Id returns the id for a Version 2 UUID or false. 67 | func (uuid UUID) Id() (uint32, bool) { 68 | if v, _ := uuid.Version(); v != 2 { 69 | return 0, false 70 | } 71 | return binary.BigEndian.Uint32(uuid[0:4]), true 72 | } 73 | 74 | func (d Domain) String() string { 75 | switch d { 76 | case Person: 77 | return "Person" 78 | case Group: 79 | return "Group" 80 | case Org: 81 | return "Org" 82 | } 83 | return fmt.Sprintf("Domain%d", int(d)) 84 | } 85 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/pborman/uuid/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // The uuid package generates and inspects UUIDs. 6 | // 7 | // UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services. 8 | package uuid 9 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/pborman/uuid/hash.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import ( 8 | "crypto/md5" 9 | "crypto/sha1" 10 | "hash" 11 | ) 12 | 13 | // Well known Name Space IDs and UUIDs 14 | var ( 15 | NameSpace_DNS = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8") 16 | NameSpace_URL = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8") 17 | NameSpace_OID = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8") 18 | NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8") 19 | NIL = Parse("00000000-0000-0000-0000-000000000000") 20 | ) 21 | 22 | // NewHash returns a new UUID dervied from the hash of space concatenated with 23 | // data generated by h. The hash should be at least 16 byte in length. The 24 | // first 16 bytes of the hash are used to form the UUID. The version of the 25 | // UUID will be the lower 4 bits of version. NewHash is used to implement 26 | // NewMD5 and NewSHA1. 27 | func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { 28 | h.Reset() 29 | h.Write(space) 30 | h.Write([]byte(data)) 31 | s := h.Sum(nil) 32 | uuid := make([]byte, 16) 33 | copy(uuid, s) 34 | uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) 35 | uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant 36 | return uuid 37 | } 38 | 39 | // NewMD5 returns a new MD5 (Version 3) UUID based on the 40 | // supplied name space and data. 41 | // 42 | // NewHash(md5.New(), space, data, 3) 43 | func NewMD5(space UUID, data []byte) UUID { 44 | return NewHash(md5.New(), space, data, 3) 45 | } 46 | 47 | // NewSHA1 returns a new SHA1 (Version 5) UUID based on the 48 | // supplied name space and data. 49 | // 50 | // NewHash(sha1.New(), space, data, 5) 51 | func NewSHA1(space UUID, data []byte) UUID { 52 | return NewHash(sha1.New(), space, data, 5) 53 | } 54 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/pborman/uuid/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import "errors" 8 | 9 | func (u UUID) MarshalJSON() ([]byte, error) { 10 | if len(u) == 0 { 11 | return []byte(`""`), nil 12 | } 13 | return []byte(`"` + u.String() + `"`), nil 14 | } 15 | 16 | func (u *UUID) UnmarshalJSON(data []byte) error { 17 | if len(data) == 0 || string(data) == `""` { 18 | return nil 19 | } 20 | if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' { 21 | return errors.New("invalid UUID format") 22 | } 23 | data = data[1 : len(data)-1] 24 | uu := Parse(string(data)) 25 | if uu == nil { 26 | return errors.New("invalid UUID format") 27 | } 28 | *u = uu 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/pborman/uuid/node.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import "net" 8 | 9 | var ( 10 | interfaces []net.Interface // cached list of interfaces 11 | ifname string // name of interface being used 12 | nodeID []byte // hardware for version 1 UUIDs 13 | ) 14 | 15 | // NodeInterface returns the name of the interface from which the NodeID was 16 | // derived. The interface "user" is returned if the NodeID was set by 17 | // SetNodeID. 18 | func NodeInterface() string { 19 | return ifname 20 | } 21 | 22 | // SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. 23 | // If name is "" then the first usable interface found will be used or a random 24 | // Node ID will be generated. If a named interface cannot be found then false 25 | // is returned. 26 | // 27 | // SetNodeInterface never fails when name is "". 28 | func SetNodeInterface(name string) bool { 29 | if interfaces == nil { 30 | var err error 31 | interfaces, err = net.Interfaces() 32 | if err != nil && name != "" { 33 | return false 34 | } 35 | } 36 | 37 | for _, ifs := range interfaces { 38 | if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { 39 | if setNodeID(ifs.HardwareAddr) { 40 | ifname = ifs.Name 41 | return true 42 | } 43 | } 44 | } 45 | 46 | // We found no interfaces with a valid hardware address. If name 47 | // does not specify a specific interface generate a random Node ID 48 | // (section 4.1.6) 49 | if name == "" { 50 | if nodeID == nil { 51 | nodeID = make([]byte, 6) 52 | } 53 | randomBits(nodeID) 54 | return true 55 | } 56 | return false 57 | } 58 | 59 | // NodeID returns a slice of a copy of the current Node ID, setting the Node ID 60 | // if not already set. 61 | func NodeID() []byte { 62 | if nodeID == nil { 63 | SetNodeInterface("") 64 | } 65 | nid := make([]byte, 6) 66 | copy(nid, nodeID) 67 | return nid 68 | } 69 | 70 | // SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes 71 | // of id are used. If id is less than 6 bytes then false is returned and the 72 | // Node ID is not set. 73 | func SetNodeID(id []byte) bool { 74 | if setNodeID(id) { 75 | ifname = "user" 76 | return true 77 | } 78 | return false 79 | } 80 | 81 | func setNodeID(id []byte) bool { 82 | if len(id) < 6 { 83 | return false 84 | } 85 | if nodeID == nil { 86 | nodeID = make([]byte, 6) 87 | } 88 | copy(nodeID, id) 89 | return true 90 | } 91 | 92 | // NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is 93 | // not valid. The NodeID is only well defined for version 1 and 2 UUIDs. 94 | func (uuid UUID) NodeID() []byte { 95 | if len(uuid) != 16 { 96 | return nil 97 | } 98 | node := make([]byte, 6) 99 | copy(node, uuid[10:]) 100 | return node 101 | } 102 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/pborman/uuid/sql.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | ) 11 | 12 | // Scan implements sql.Scanner so UUIDs can be read from databases transparently 13 | // Currently, database types that map to string and []byte are supported. Please 14 | // consult database-specific driver documentation for matching types. 15 | func (uuid *UUID) Scan(src interface{}) error { 16 | switch src.(type) { 17 | case string: 18 | // see uuid.Parse for required string format 19 | parsed := Parse(src.(string)) 20 | 21 | if parsed == nil { 22 | return errors.New("Scan: invalid UUID format") 23 | } 24 | 25 | *uuid = parsed 26 | case []byte: 27 | b := src.([]byte) 28 | 29 | // assumes a simple slice of bytes if 16 bytes 30 | // otherwise attempts to parse 31 | if len(b) == 16 { 32 | *uuid = UUID(b) 33 | } else { 34 | u := Parse(string(b)) 35 | 36 | if u == nil { 37 | return errors.New("Scan: invalid UUID format") 38 | } 39 | 40 | *uuid = u 41 | } 42 | 43 | default: 44 | return fmt.Errorf("Scan: unable to scan type %T into UUID", src) 45 | } 46 | 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/pborman/uuid/time.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import ( 8 | "encoding/binary" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | // A Time represents a time as the number of 100's of nanoseconds since 15 Oct 14 | // 1582. 15 | type Time int64 16 | 17 | const ( 18 | lillian = 2299160 // Julian day of 15 Oct 1582 19 | unix = 2440587 // Julian day of 1 Jan 1970 20 | epoch = unix - lillian // Days between epochs 21 | g1582 = epoch * 86400 // seconds between epochs 22 | g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs 23 | ) 24 | 25 | var ( 26 | mu sync.Mutex 27 | lasttime uint64 // last time we returned 28 | clock_seq uint16 // clock sequence for this run 29 | 30 | timeNow = time.Now // for testing 31 | ) 32 | 33 | // UnixTime converts t the number of seconds and nanoseconds using the Unix 34 | // epoch of 1 Jan 1970. 35 | func (t Time) UnixTime() (sec, nsec int64) { 36 | sec = int64(t - g1582ns100) 37 | nsec = (sec % 10000000) * 100 38 | sec /= 10000000 39 | return sec, nsec 40 | } 41 | 42 | // GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and 43 | // clock sequence as well as adjusting the clock sequence as needed. An error 44 | // is returned if the current time cannot be determined. 45 | func GetTime() (Time, uint16, error) { 46 | defer mu.Unlock() 47 | mu.Lock() 48 | return getTime() 49 | } 50 | 51 | func getTime() (Time, uint16, error) { 52 | t := timeNow() 53 | 54 | // If we don't have a clock sequence already, set one. 55 | if clock_seq == 0 { 56 | setClockSequence(-1) 57 | } 58 | now := uint64(t.UnixNano()/100) + g1582ns100 59 | 60 | // If time has gone backwards with this clock sequence then we 61 | // increment the clock sequence 62 | if now <= lasttime { 63 | clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000 64 | } 65 | lasttime = now 66 | return Time(now), clock_seq, nil 67 | } 68 | 69 | // ClockSequence returns the current clock sequence, generating one if not 70 | // already set. The clock sequence is only used for Version 1 UUIDs. 71 | // 72 | // The uuid package does not use global static storage for the clock sequence or 73 | // the last time a UUID was generated. Unless SetClockSequence a new random 74 | // clock sequence is generated the first time a clock sequence is requested by 75 | // ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated 76 | // for 77 | func ClockSequence() int { 78 | defer mu.Unlock() 79 | mu.Lock() 80 | return clockSequence() 81 | } 82 | 83 | func clockSequence() int { 84 | if clock_seq == 0 { 85 | setClockSequence(-1) 86 | } 87 | return int(clock_seq & 0x3fff) 88 | } 89 | 90 | // SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to 91 | // -1 causes a new sequence to be generated. 92 | func SetClockSequence(seq int) { 93 | defer mu.Unlock() 94 | mu.Lock() 95 | setClockSequence(seq) 96 | } 97 | 98 | func setClockSequence(seq int) { 99 | if seq == -1 { 100 | var b [2]byte 101 | randomBits(b[:]) // clock sequence 102 | seq = int(b[0])<<8 | int(b[1]) 103 | } 104 | old_seq := clock_seq 105 | clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant 106 | if old_seq != clock_seq { 107 | lasttime = 0 108 | } 109 | } 110 | 111 | // Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in 112 | // uuid. It returns false if uuid is not valid. The time is only well defined 113 | // for version 1 and 2 UUIDs. 114 | func (uuid UUID) Time() (Time, bool) { 115 | if len(uuid) != 16 { 116 | return 0, false 117 | } 118 | time := int64(binary.BigEndian.Uint32(uuid[0:4])) 119 | time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 120 | time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 121 | return Time(time), true 122 | } 123 | 124 | // ClockSequence returns the clock sequence encoded in uuid. It returns false 125 | // if uuid is not valid. The clock sequence is only well defined for version 1 126 | // and 2 UUIDs. 127 | func (uuid UUID) ClockSequence() (int, bool) { 128 | if len(uuid) != 16 { 129 | return 0, false 130 | } 131 | return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true 132 | } 133 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/pborman/uuid/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import ( 8 | "io" 9 | ) 10 | 11 | // randomBits completely fills slice b with random data. 12 | func randomBits(b []byte) { 13 | if _, err := io.ReadFull(rander, b); err != nil { 14 | panic(err.Error()) // rand should never fail 15 | } 16 | } 17 | 18 | // xvalues returns the value of a byte as a hexadecimal digit or 255. 19 | var xvalues = []byte{ 20 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 21 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 22 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 23 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 24 | 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 25 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 26 | 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 27 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 28 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 29 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 30 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 31 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 32 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 33 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 34 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 35 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 36 | } 37 | 38 | // xtob converts the the first two hex bytes of x into a byte. 39 | func xtob(x string) (byte, bool) { 40 | b1 := xvalues[x[0]] 41 | b2 := xvalues[x[1]] 42 | return (b1 << 4) | b2, b1 != 255 && b2 != 255 43 | } 44 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/pborman/uuid/uuid.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import ( 8 | "bytes" 9 | "crypto/rand" 10 | "fmt" 11 | "io" 12 | "strings" 13 | ) 14 | 15 | // A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC 16 | // 4122. 17 | type UUID []byte 18 | 19 | // A Version represents a UUIDs version. 20 | type Version byte 21 | 22 | // A Variant represents a UUIDs variant. 23 | type Variant byte 24 | 25 | // Constants returned by Variant. 26 | const ( 27 | Invalid = Variant(iota) // Invalid UUID 28 | RFC4122 // The variant specified in RFC4122 29 | Reserved // Reserved, NCS backward compatibility. 30 | Microsoft // Reserved, Microsoft Corporation backward compatibility. 31 | Future // Reserved for future definition. 32 | ) 33 | 34 | var rander = rand.Reader // random function 35 | 36 | // New returns a new random (version 4) UUID as a string. It is a convenience 37 | // function for NewRandom().String(). 38 | func New() string { 39 | return NewRandom().String() 40 | } 41 | 42 | // Parse decodes s into a UUID or returns nil. Both the UUID form of 43 | // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and 44 | // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded. 45 | func Parse(s string) UUID { 46 | if len(s) == 36+9 { 47 | if strings.ToLower(s[:9]) != "urn:uuid:" { 48 | return nil 49 | } 50 | s = s[9:] 51 | } else if len(s) != 36 { 52 | return nil 53 | } 54 | if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { 55 | return nil 56 | } 57 | uuid := make([]byte, 16) 58 | for i, x := range []int{ 59 | 0, 2, 4, 6, 60 | 9, 11, 61 | 14, 16, 62 | 19, 21, 63 | 24, 26, 28, 30, 32, 34} { 64 | if v, ok := xtob(s[x:]); !ok { 65 | return nil 66 | } else { 67 | uuid[i] = v 68 | } 69 | } 70 | return uuid 71 | } 72 | 73 | // Equal returns true if uuid1 and uuid2 are equal. 74 | func Equal(uuid1, uuid2 UUID) bool { 75 | return bytes.Equal(uuid1, uuid2) 76 | } 77 | 78 | // String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 79 | // , or "" if uuid is invalid. 80 | func (uuid UUID) String() string { 81 | if uuid == nil || len(uuid) != 16 { 82 | return "" 83 | } 84 | b := []byte(uuid) 85 | return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x", 86 | b[:4], b[4:6], b[6:8], b[8:10], b[10:]) 87 | } 88 | 89 | // URN returns the RFC 2141 URN form of uuid, 90 | // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. 91 | func (uuid UUID) URN() string { 92 | if uuid == nil || len(uuid) != 16 { 93 | return "" 94 | } 95 | b := []byte(uuid) 96 | return fmt.Sprintf("urn:uuid:%08x-%04x-%04x-%04x-%012x", 97 | b[:4], b[4:6], b[6:8], b[8:10], b[10:]) 98 | } 99 | 100 | // Variant returns the variant encoded in uuid. It returns Invalid if 101 | // uuid is invalid. 102 | func (uuid UUID) Variant() Variant { 103 | if len(uuid) != 16 { 104 | return Invalid 105 | } 106 | switch { 107 | case (uuid[8] & 0xc0) == 0x80: 108 | return RFC4122 109 | case (uuid[8] & 0xe0) == 0xc0: 110 | return Microsoft 111 | case (uuid[8] & 0xe0) == 0xe0: 112 | return Future 113 | default: 114 | return Reserved 115 | } 116 | panic("unreachable") 117 | } 118 | 119 | // Version returns the verison of uuid. It returns false if uuid is not 120 | // valid. 121 | func (uuid UUID) Version() (Version, bool) { 122 | if len(uuid) != 16 { 123 | return 0, false 124 | } 125 | return Version(uuid[6] >> 4), true 126 | } 127 | 128 | func (v Version) String() string { 129 | if v > 15 { 130 | return fmt.Sprintf("BAD_VERSION_%d", v) 131 | } 132 | return fmt.Sprintf("VERSION_%d", v) 133 | } 134 | 135 | func (v Variant) String() string { 136 | switch v { 137 | case RFC4122: 138 | return "RFC4122" 139 | case Reserved: 140 | return "Reserved" 141 | case Microsoft: 142 | return "Microsoft" 143 | case Future: 144 | return "Future" 145 | case Invalid: 146 | return "Invalid" 147 | } 148 | return fmt.Sprintf("BadVariant%d", int(v)) 149 | } 150 | 151 | // SetRand sets the random number generator to r, which implents io.Reader. 152 | // If r.Read returns an error when the package requests random data then 153 | // a panic will be issued. 154 | // 155 | // Calling SetRand with nil sets the random number generator to the default 156 | // generator. 157 | func SetRand(r io.Reader) { 158 | if r == nil { 159 | rander = rand.Reader 160 | return 161 | } 162 | rander = r 163 | } 164 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/pborman/uuid/version1.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | import ( 8 | "encoding/binary" 9 | ) 10 | 11 | // NewUUID returns a Version 1 UUID based on the current NodeID and clock 12 | // sequence, and the current time. If the NodeID has not been set by SetNodeID 13 | // or SetNodeInterface then it will be set automatically. If the NodeID cannot 14 | // be set NewUUID returns nil. If clock sequence has not been set by 15 | // SetClockSequence then it will be set automatically. If GetTime fails to 16 | // return the current NewUUID returns nil. 17 | func NewUUID() UUID { 18 | if nodeID == nil { 19 | SetNodeInterface("") 20 | } 21 | 22 | now, seq, err := GetTime() 23 | if err != nil { 24 | return nil 25 | } 26 | 27 | uuid := make([]byte, 16) 28 | 29 | time_low := uint32(now & 0xffffffff) 30 | time_mid := uint16((now >> 32) & 0xffff) 31 | time_hi := uint16((now >> 48) & 0x0fff) 32 | time_hi |= 0x1000 // Version 1 33 | 34 | binary.BigEndian.PutUint32(uuid[0:], time_low) 35 | binary.BigEndian.PutUint16(uuid[4:], time_mid) 36 | binary.BigEndian.PutUint16(uuid[6:], time_hi) 37 | binary.BigEndian.PutUint16(uuid[8:], seq) 38 | copy(uuid[10:], nodeID) 39 | 40 | return uuid 41 | } 42 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/pborman/uuid/version4.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package uuid 6 | 7 | // Random returns a Random (Version 4) UUID or panics. 8 | // 9 | // The strength of the UUIDs is based on the strength of the crypto/rand 10 | // package. 11 | // 12 | // A note about uniqueness derived from from the UUID Wikipedia entry: 13 | // 14 | // Randomly generated UUIDs have 122 random bits. One's annual risk of being 15 | // hit by a meteorite is estimated to be one chance in 17 billion, that 16 | // means the probability is about 0.00000000006 (6 × 10−11), 17 | // equivalent to the odds of creating a few tens of trillions of UUIDs in a 18 | // year and having one duplicate. 19 | func NewRandom() UUID { 20 | uuid := make([]byte, 16) 21 | randomBits([]byte(uuid)) 22 | uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 23 | uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 24 | return uuid 25 | } 26 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/image/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/image/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/image/font/font.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package font defines an interface for font faces, for drawing text on an 6 | // image. 7 | // 8 | // Other packages provide font face implementations. For example, a truetype 9 | // package would provide one based on .ttf font files. 10 | package font 11 | 12 | import ( 13 | "image" 14 | "image/draw" 15 | "io" 16 | 17 | "github.com/kevinburke/rct/Godeps/_workspace/src/golang.org/x/image/math/fixed" 18 | ) 19 | 20 | // TODO: who is responsible for caches (glyph images, glyph indices, kerns)? 21 | // The Drawer or the Face? 22 | 23 | // Face is a font face. Its glyphs are often derived from a font file, such as 24 | // "Comic_Sans_MS.ttf", but a face has a specific size, style, weight and 25 | // hinting. For example, the 12pt and 18pt versions of Comic Sans are two 26 | // different faces, even if derived from the same font file. 27 | // 28 | // A Face is not safe for concurrent use by multiple goroutines, as its methods 29 | // may re-use implementation-specific caches and mask image buffers. 30 | // 31 | // To create a Face, look to other packages that implement specific font file 32 | // formats. 33 | type Face interface { 34 | io.Closer 35 | 36 | // Glyph returns the draw.DrawMask parameters (dr, mask, maskp) to draw r's 37 | // glyph at the sub-pixel destination location dot, and that glyph's 38 | // advance width. 39 | // 40 | // It returns !ok if the face does not contain a glyph for r. 41 | // 42 | // The contents of the mask image returned by one Glyph call may change 43 | // after the next Glyph call. Callers that want to cache the mask must make 44 | // a copy. 45 | Glyph(dot fixed.Point26_6, r rune) ( 46 | dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) 47 | 48 | // GlyphBounds returns the bounding box of r's glyph, drawn at a dot equal 49 | // to the origin, and that glyph's advance width. 50 | // 51 | // It returns !ok if the face does not contain a glyph for r. 52 | // 53 | // The glyph's ascent and descent equal -bounds.Min.Y and +bounds.Max.Y. A 54 | // visual depiction of what these metrics are is at 55 | // https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyph_metrics_2x.png 56 | GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) 57 | 58 | // GlyphAdvance returns the advance width of r's glyph. 59 | // 60 | // It returns !ok if the face does not contain a glyph for r. 61 | GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) 62 | 63 | // Kern returns the horizontal adjustment for the kerning pair (r0, r1). A 64 | // positive kern means to move the glyphs further apart. 65 | Kern(r0, r1 rune) fixed.Int26_6 66 | 67 | // TODO: per-font Metrics. 68 | // TODO: ColoredGlyph for various emoji? 69 | // TODO: Ligatures? Shaping? 70 | } 71 | 72 | // TODO: Drawer.Layout or Drawer.Measure methods to measure text without 73 | // drawing? 74 | 75 | // Drawer draws text on a destination image. 76 | // 77 | // A Drawer is not safe for concurrent use by multiple goroutines, since its 78 | // Face is not. 79 | type Drawer struct { 80 | // Dst is the destination image. 81 | Dst draw.Image 82 | // Src is the source image. 83 | Src image.Image 84 | // Face provides the glyph mask images. 85 | Face Face 86 | // Dot is the baseline location to draw the next glyph. The majority of the 87 | // affected pixels will be above and to the right of the dot, but some may 88 | // be below or to the left. For example, drawing a 'j' in an italic face 89 | // may affect pixels below and to the left of the dot. 90 | Dot fixed.Point26_6 91 | 92 | // TODO: Clip image.Image? 93 | // TODO: SrcP image.Point for Src images other than *image.Uniform? How 94 | // does it get updated during DrawString? 95 | } 96 | 97 | // TODO: should DrawString return the last rune drawn, so the next DrawString 98 | // call can kern beforehand? Or should that be the responsibility of the caller 99 | // if they really want to do that, since they have to explicitly shift d.Dot 100 | // anyway? 101 | // 102 | // In general, we'd have a DrawBytes([]byte) and DrawRuneReader(io.RuneReader) 103 | // and the last case can't assume that you can rewind the stream. 104 | // 105 | // TODO: how does this work with line breaking: drawing text up until a 106 | // vertical line? Should DrawString return the number of runes drawn? 107 | 108 | // DrawString draws s at the dot and advances the dot's location. 109 | func (d *Drawer) DrawString(s string) { 110 | var prevC rune 111 | for i, c := range s { 112 | if i != 0 { 113 | d.Dot.X += d.Face.Kern(prevC, c) 114 | } 115 | dr, mask, maskp, advance, ok := d.Face.Glyph(d.Dot, c) 116 | if !ok { 117 | // TODO: is falling back on the U+FFFD glyph the responsibility of 118 | // the Drawer or the Face? 119 | // TODO: set prevC = '\ufffd'? 120 | continue 121 | } 122 | draw.DrawMask(d.Dst, dr, d.Src, image.Point{}, mask, maskp, draw.Over) 123 | d.Dot.X += advance 124 | prevC = c 125 | } 126 | } 127 | 128 | // MeasureString returns how far dot would advance by drawing s. 129 | func (d *Drawer) MeasureString(s string) (advance fixed.Int26_6) { 130 | var prevC rune 131 | for i, c := range s { 132 | if i != 0 { 133 | advance += d.Face.Kern(prevC, c) 134 | } 135 | a, ok := d.Face.GlyphAdvance(c) 136 | if !ok { 137 | // TODO: is falling back on the U+FFFD glyph the responsibility of 138 | // the Drawer or the Face? 139 | // TODO: set prevC = '\ufffd'? 140 | continue 141 | } 142 | advance += a 143 | prevC = c 144 | } 145 | return advance 146 | } 147 | 148 | // Hinting selects how to quantize a vector font's glyph nodes. 149 | // 150 | // Not all fonts support hinting. 151 | type Hinting int 152 | 153 | const ( 154 | HintingNone Hinting = iota 155 | HintingVertical 156 | HintingFull 157 | ) 158 | 159 | // Stretch selects a normal, condensed, or expanded face. 160 | // 161 | // Not all fonts support stretches. 162 | type Stretch int 163 | 164 | const ( 165 | StretchUltraCondensed Stretch = -4 166 | StretchExtraCondensed Stretch = -3 167 | StretchCondensed Stretch = -2 168 | StretchSemiCondensed Stretch = -1 169 | StretchNormal Stretch = +0 170 | StretchSemiExpanded Stretch = +1 171 | StretchExpanded Stretch = +2 172 | StretchExtraExpanded Stretch = +3 173 | StretchUltraExpanded Stretch = +4 174 | ) 175 | 176 | // Style selects a normal, italic, or oblique face. 177 | // 178 | // Not all fonts support styles. 179 | type Style int 180 | 181 | const ( 182 | StyleNormal Style = iota 183 | StyleItalic 184 | StyleOblique 185 | ) 186 | 187 | // Weight selects a normal, light or bold face. 188 | // 189 | // Not all fonts support weights. 190 | type Weight int 191 | 192 | const ( 193 | WeightThin Weight = 100 194 | WeightExtraLight Weight = 200 195 | WeightLight Weight = 300 196 | WeightNormal Weight = 400 197 | WeightMedium Weight = 500 198 | WeightSemiBold Weight = 600 199 | WeightBold Weight = 700 200 | WeightExtraBold Weight = 800 201 | WeightBlack Weight = 900 202 | ) 203 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/golang.org/x/image/math/fixed/fixed.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package fixed implements fixed-point integer types. 6 | package fixed 7 | 8 | import ( 9 | "fmt" 10 | ) 11 | 12 | // TODO: implement fmt.Formatter for %f and %g. 13 | 14 | // I returns the integer value i as an Int26_6. 15 | // 16 | // For example, passing the integer value 2 yields Int26_6(128). 17 | func I(i int) Int26_6 { 18 | return Int26_6(i << 6) 19 | } 20 | 21 | // Int26_6 is a signed 26.6 fixed-point number. 22 | // 23 | // The integer part ranges from -33554432 to 33554431, inclusive. The 24 | // fractional part has 6 bits of precision. 25 | // 26 | // For example, the number one-and-a-quarter is Int26_6(1<<6 + 1<<4). 27 | type Int26_6 int32 28 | 29 | // String returns a human-readable representation of a 26.6 fixed-point number. 30 | // 31 | // For example, the number one-and-a-quarter becomes "1:16". 32 | func (x Int26_6) String() string { 33 | const shift, mask = 6, 1<<6 - 1 34 | if x >= 0 { 35 | return fmt.Sprintf("%d:%02d", int32(x>>shift), int32(x&mask)) 36 | } 37 | x = -x 38 | if x >= 0 { 39 | return fmt.Sprintf("-%d:%02d", int32(x>>shift), int32(x&mask)) 40 | } 41 | return "-33554432:00" // The minimum value is -(1<<25). 42 | } 43 | 44 | // Int52_12 is a signed 52.12 fixed-point number. 45 | // 46 | // The integer part ranges from -2251799813685248 to 2251799813685247, 47 | // inclusive. The fractional part has 12 bits of precision. 48 | // 49 | // For example, the number one-and-a-quarter is Int52_12(1<<12 + 1<<10). 50 | type Int52_12 int64 51 | 52 | // String returns a human-readable representation of a 52.12 fixed-point 53 | // number. 54 | // 55 | // For example, the number one-and-a-quarter becomes "1:1024". 56 | func (x Int52_12) String() string { 57 | const shift, mask = 12, 1<<12 - 1 58 | if x >= 0 { 59 | return fmt.Sprintf("%d:%04d", int64(x>>shift), int64(x&mask)) 60 | } 61 | x = -x 62 | if x >= 0 { 63 | return fmt.Sprintf("-%d:%04d", int64(x>>shift), int64(x&mask)) 64 | } 65 | return "-2251799813685248:0000" // The minimum value is -(1<<51). 66 | } 67 | 68 | // P returns the integer values x and y as a Point26_6. 69 | // 70 | // For example, passing the integer values (2, -3) yields Point26_6{128, -192}. 71 | func P(x, y int) Point26_6 { 72 | return Point26_6{Int26_6(x << 6), Int26_6(y << 6)} 73 | } 74 | 75 | // Point26_6 is a 26.6 fixed-point coordinate pair. 76 | // 77 | // It is analogous to the image.Point type in the standard library. 78 | type Point26_6 struct { 79 | X, Y Int26_6 80 | } 81 | 82 | // Add returns the vector p+q. 83 | func (p Point26_6) Add(q Point26_6) Point26_6 { 84 | return Point26_6{p.X + q.X, p.Y + q.Y} 85 | } 86 | 87 | // Sub returns the vector p-q. 88 | func (p Point26_6) Sub(q Point26_6) Point26_6 { 89 | return Point26_6{p.X - q.X, p.Y - q.Y} 90 | } 91 | 92 | // Mul returns the vector p*k. 93 | func (p Point26_6) Mul(k Int26_6) Point26_6 { 94 | return Point26_6{p.X * k / 64, p.Y * k / 64} 95 | } 96 | 97 | // Div returns the vector p/k. 98 | func (p Point26_6) Div(k Int26_6) Point26_6 { 99 | return Point26_6{p.X * 64 / k, p.Y * 64 / k} 100 | } 101 | 102 | // Point52_12 is a 52.12 fixed-point coordinate pair. 103 | // 104 | // It is analogous to the image.Point type in the standard library. 105 | type Point52_12 struct { 106 | X, Y Int52_12 107 | } 108 | 109 | // Add returns the vector p+q. 110 | func (p Point52_12) Add(q Point52_12) Point52_12 { 111 | return Point52_12{p.X + q.X, p.Y + q.Y} 112 | } 113 | 114 | // Sub returns the vector p-q. 115 | func (p Point52_12) Sub(q Point52_12) Point52_12 { 116 | return Point52_12{p.X - q.X, p.Y - q.Y} 117 | } 118 | 119 | // Mul returns the vector p*k. 120 | func (p Point52_12) Mul(k Int52_12) Point52_12 { 121 | return Point52_12{p.X * k / 4096, p.Y * k / 4096} 122 | } 123 | 124 | // Div returns the vector p/k. 125 | func (p Point52_12) Div(k Int52_12) Point52_12 { 126 | return Point52_12{p.X * 4096 / k, p.Y * 4096 / k} 127 | } 128 | 129 | // R returns the integer values minX, minY, maxX, maxY as a Rectangle26_6. 130 | // 131 | // For example, passing the integer values (0, 1, 2, 3) yields 132 | // Rectangle26_6{Point26_6{0, 64}, Point26_6{128, 192}}. 133 | // 134 | // Like the image.Rect function in the standard library, the returned rectangle 135 | // has minimum and maximum coordinates swapped if necessary so that it is 136 | // well-formed. 137 | func R(minX, minY, maxX, maxY int) Rectangle26_6 { 138 | if minX > maxX { 139 | minX, maxX = maxX, minX 140 | } 141 | if minY > maxY { 142 | minY, maxY = maxY, minY 143 | } 144 | return Rectangle26_6{ 145 | Point26_6{ 146 | Int26_6(minX << 6), 147 | Int26_6(minY << 6), 148 | }, 149 | Point26_6{ 150 | Int26_6(maxX << 6), 151 | Int26_6(maxY << 6), 152 | }, 153 | } 154 | } 155 | 156 | // Rectangle26_6 is a 26.6 fixed-point coordinate rectangle. The Min bound is 157 | // inclusive and the Max bound is exclusive. It is well-formed if Min.X <= 158 | // Max.X and likewise for Y. 159 | // 160 | // It is analogous to the image.Rectangle type in the standard library. 161 | type Rectangle26_6 struct { 162 | Min, Max Point26_6 163 | } 164 | 165 | // Rectangle52_12 is a 52.12 fixed-point coordinate rectangle. The Min bound is 166 | // inclusive and the Max bound is exclusive. It is well-formed if Min.X <= 167 | // Max.X and likewise for Y. 168 | // 169 | // It is analogous to the image.Rectangle type in the standard library. 170 | type Rectangle52_12 struct { 171 | Min, Max Point52_12 172 | } 173 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Kevin Burke. 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test serve install experiment 2 | 3 | # need to skip the wip directory 4 | 5 | all: install test 6 | 7 | install: 8 | go install github.com/jmhodges/justrun 9 | go install ./bits/... ./genetic/... ./geo/... ./image/... ./rle/... ./server/... ./td6/... ./tracks/... 10 | 11 | test: 12 | go test -race -timeout 1s \ 13 | ./bits/... \ 14 | ./genetic/... \ 15 | ./geo/... \ 16 | ./image/... \ 17 | ./physics/... \ 18 | ./rle/... \ 19 | ./server/... \ 20 | ./td6/... \ 21 | ./tracks/... 22 | 23 | serve: 24 | find . -name '*.go' -o -name '*.html' | justrun -stdin -v=true -c 'go run server/main.go --template-directory="$(PWD)/server/templates" --static-directory="$(PWD)/server/static"' 25 | 26 | experiment: install 27 | run_experiment --package-root ~/code/go/src/github.com/kevinburke/rct 28 | 29 | compress: install 30 | get_old_experiments | bash scripts/compress.bash 31 | -------------------------------------------------------------------------------- /bits/bits.go: -------------------------------------------------------------------------------- 1 | // Functions for manipulating bits 2 | package bits 3 | 4 | func On(n int, pos uint) bool { 5 | val := n & (1 << pos) 6 | return (val > 0) 7 | } 8 | 9 | // Sets the bit at pos in the integer n. 10 | func Set(n int, pos uint) int { 11 | n |= (1 << pos) 12 | return n 13 | } 14 | 15 | // Only set the bit if the conditional is true. 16 | func SetCond(n int, pos uint, question bool) int { 17 | if question { 18 | n |= (1 << pos) 19 | } 20 | return n 21 | } 22 | 23 | // Clears the bit at pos in n. 24 | func Clear(n int, pos uint) int { 25 | mask := ^(1 << pos) 26 | n &= mask 27 | return n 28 | } 29 | -------------------------------------------------------------------------------- /doc/max_speed.md: -------------------------------------------------------------------------------- 1 | edi+0x51 is referenced in several functions: 2 | 3 | - 0x67AA18 (drawing?) 4 | - 0x67C43A 5 | - 0x67E1AA 6 | - 0x68017E 7 | -------------------------------------------------------------------------------- /doc/ride_ids.md: -------------------------------------------------------------------------------- 1 | 1. some value is stored in ride_error_string 2 | 3 | 2. 0x100 is subtracted from the value 4 | 5 | 3. the value is multiplied by 2 6 | 7 | 4. this is an index into ride_configuration_string_ids 8 | 9 | example: 159h 10 | 11 | - subtract 0x100 to get 0x59 12 | 13 | - multiply by 2 to get 0xb2 14 | 15 | get pointer in ride_configuration_string_ids 16 | 17 | ## reverse 18 | 19 | start with 0xb2 20 | 21 | - divide by 2 to get 0x59 (?) 22 | 23 | - add 0x100 24 | -------------------------------------------------------------------------------- /exe_reader/element_hex_codes/print_all_hex_values.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/kevinburke/rct/tracks" 7 | ) 8 | 9 | func main() { 10 | // Prints out element names and their hex values 11 | for i, elem := range tracks.ElementNames { 12 | // these are auto-padded to the correct spots 13 | fmt.Printf("\"%s\", // %X\n", elem, i) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /exe_reader/print_tracks/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/kevinburke/rct/exe_reader" 7 | "github.com/kevinburke/rct/tracks" 8 | ) 9 | 10 | // taken from track_data.h 11 | const RCT_DIRECTION_ADDR = 0x005968BB 12 | const RCT_DIRECTION_WIDTH = 10 13 | 14 | const RCT_BANK_SLOPE_ADDR = 0x00597c9d 15 | const RCT_BANK_SLOPE_WIDTH = 8 16 | 17 | // Follows the format in TrackCoordinates 18 | /* 19 | sint8 rotation_negative; // 0x00 20 | sint8 rotation_positive; // 0x01 21 | sint16 z_negative; // 0x02 22 | sint16 z_positive; // 0x04 23 | sint16 x; // 0x06 24 | 25 | // my sideways delta 26 | sint16 y; // 0x08 27 | */ 28 | const RCT_FORWARD_ADDR = 0x005968bb 29 | const RCT_FORWARD_WIDTH = 0x0A 30 | 31 | func main() { 32 | f, err := os.Open(os.Getenv("HOME") + "/code/OpenRCT2/openrct2.exe") 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | defer f.Close() 38 | 39 | b := make([]byte, 256*RCT_DIRECTION_WIDTH) 40 | f.ReadAt(b, int64(RCT_DIRECTION_ADDR)) 41 | 42 | c := make([]byte, 256*RCT_BANK_SLOPE_WIDTH) 43 | f.ReadAt(c, RCT_BANK_SLOPE_ADDR) 44 | 45 | d := make([]byte, 256*RCT_FORWARD_WIDTH) 46 | f.ReadAt(d, RCT_FORWARD_ADDR) 47 | 48 | for i := 0; i < len(tracks.ElementNames); i++ { 49 | //fmt.Println(i) 50 | //fmt.Printf("%55s ", tracks.ElementNames[i]) 51 | //fmt.Printf("%4d ", b[i*WIDTH]) 52 | //fmt.Printf("\n") 53 | idx := i * RCT_DIRECTION_WIDTH 54 | bitSubset := b[idx : idx+RCT_DIRECTION_WIDTH] 55 | // xxx - there are 2 pieces to direction change, the first one is for 56 | // diagonal to straight. we're ignoring diagonals for the moment. 57 | //fmt.Printf("%s: %v\n", tracks.ElementNames[i], bitSubset[0]) 58 | 59 | //fmt.Printf("%s: %v\n", tracks.ElementNames[i], bitSubset[8]) 60 | 61 | bankIdx := i * RCT_BANK_SLOPE_WIDTH 62 | bankBitSubset := c[bankIdx : bankIdx+RCT_BANK_SLOPE_WIDTH] 63 | 64 | forwardIdx := i * RCT_FORWARD_WIDTH 65 | forwardBitSubset := d[forwardIdx : forwardIdx+RCT_FORWARD_WIDTH] 66 | 67 | exe_reader.PrintValues(i, tracks.ElementNames[i], int(bitSubset[1]), int(bitSubset[8]), int(bitSubset[2]), int(bitSubset[4]), bankBitSubset, forwardBitSubset) 68 | } 69 | 70 | //fmt.Printf("%#v\n", tracks.TS_MAP) 71 | //fmt.Printf("%T\n", tracks.TS_MAP) 72 | } 73 | -------------------------------------------------------------------------------- /exe_reader/track_values.go: -------------------------------------------------------------------------------- 1 | package exe_reader 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | 8 | "github.com/kevinburke/rct/bits" 9 | "github.com/kevinburke/rct/tracks" 10 | ) 11 | 12 | // XXX, this doesn't correctly handle s-bends, which only move sideways by 1 13 | // piece, I think. 14 | func SidewaysDelta(sidewaysDeltaByte int) int { 15 | if sidewaysDeltaByte == 0 { 16 | return 0 17 | } 18 | if bits.On(sidewaysDeltaByte, 7) { 19 | return 1 + (256-sidewaysDeltaByte)>>5 20 | } 21 | return -(1 + sidewaysDeltaByte>>5) 22 | } 23 | 24 | var reverseMap = map[tracks.DirectionDelta]string{ 25 | tracks.DIR_STRAIGHT: "DIR_STRAIGHT", 26 | tracks.DIR_45_DEG_RIGHT: "DIR_45_DEG_RIGHT", 27 | tracks.DIR_90_DEG_RIGHT: "DIR_90_DEG_RIGHT", 28 | tracks.DIR_180_DEG: "DIR_180_DEG", 29 | tracks.DIR_90_DEG_LEFT: "DIR_90_DEG_LEFT", 30 | tracks.DIR_45_DEG_LEFT: "DIR_45_DEG_LEFT", 31 | // XXX - fix when we allow diagonals. 32 | //tracks.DIR_DIAGONAL: "DIR_DIAGONAL", 33 | tracks.DIR_DIAGONAL_LEFT: "DIR_DIAGONAL_LEFT", 34 | tracks.DIR_DIAGONAL_RIGHT: "DIR_DIAGONAL_RIGHT", 35 | } 36 | 37 | func GetDiagonalFromRCTStruct(b []byte) tracks.DirectionDelta { 38 | startingDirectionInt := int(b[0]) 39 | startingDirection := tracks.RCTDirectionKeys[startingDirectionInt] 40 | endingDirectionInt := int(b[1]) 41 | endingDirection := tracks.RCTDirectionKeys[endingDirectionInt] 42 | 43 | if startingDirection == tracks.DIR_DIAGONAL { 44 | if endingDirection == tracks.DIR_DIAGONAL { 45 | return tracks.DIR_DIAGONAL 46 | } else if endingDirection == tracks.DIR_90_DEG_RIGHT { 47 | return tracks.DIR_DIAGONAL_RIGHT 48 | } else { 49 | return tracks.DIR_DIAGONAL_LEFT 50 | } 51 | } else if endingDirection == tracks.DIR_DIAGONAL { 52 | return tracks.DIR_DIAGONAL_RIGHT 53 | } else { 54 | return endingDirection 55 | } 56 | } 57 | 58 | func ElevationDelta(positiveHeightBit int, negativeHeightBit int) int { 59 | if positiveHeightBit > 0 { 60 | return positiveHeightBit >> 3 61 | } 62 | if negativeHeightBit > 0 { 63 | return -(negativeHeightBit >> 3) 64 | } 65 | return 0 66 | } 67 | 68 | var PRINT = true 69 | 70 | func pt(format string, val ...interface{}) { 71 | if PRINT { 72 | fmt.Printf(format, val...) 73 | } 74 | } 75 | 76 | func DirectionDelta(directionbit int) tracks.DirectionDelta { 77 | if directionbit == 0 { 78 | return tracks.DIR_STRAIGHT 79 | } else if directionbit == 1 { 80 | return tracks.DIR_90_DEG_RIGHT 81 | } else if directionbit == 2 { 82 | return tracks.DIR_180_DEG 83 | } else if directionbit == 3 { 84 | return tracks.DIR_90_DEG_LEFT 85 | } else { 86 | // XXX requires parsing more bits, see TrackCoordinates in track_data.h 87 | return tracks.DIR_DIAGONAL 88 | } 89 | } 90 | 91 | // Print out the Go code to make up a track segment 92 | // XXX actually build the Go datatypes instead of printing/needing to copy 93 | // paste 94 | func PrintValues(typ int, elementName string, direction int, sideways int, negativeHeightBit int, positiveHeightBit int, bankByte []byte, forwardByte []byte) { 95 | 96 | dir := DirectionDelta(direction) 97 | sidewaysDelta := SidewaysDelta(sideways) 98 | elevationDelta := ElevationDelta(positiveHeightBit, negativeHeightBit) 99 | 100 | pt("%s: &Segment{\n", elementName) 101 | 102 | pt("\tType: %#x,\n", typ) 103 | bitVal := int(bankByte[2]) 104 | pt("\tInputDegree: %s,\n", configTable1Map[2][bitVal]) 105 | bitVal = int(bankByte[1]) 106 | pt("\tOutputDegree: %s,\n", configTable1Map[1][bitVal]) 107 | bitVal = int(bankByte[4]) 108 | pt("\tStartingBank: %s,\n", configTable1Map[4][bitVal]) 109 | bitVal = int(bankByte[3]) 110 | pt("\tEndingBank: %s,\n", configTable1Map[3][bitVal]) 111 | 112 | var forward int16 113 | buf := bytes.NewReader(forwardByte[6:8]) 114 | err := binary.Read(buf, binary.LittleEndian, &forward) 115 | if err != nil { 116 | panic(err) 117 | } 118 | 119 | pt("\tForwardDelta: %d,\n", forward/-32+1) 120 | 121 | pt("\tDirectionDelta: %s,\n", reverseMap[dir]) 122 | pt("\tSidewaysDelta: %d,\n", sidewaysDelta) 123 | pt("\tElevationDelta: %d,\n", elevationDelta) 124 | pt("},\n") 125 | } 126 | 127 | // Follows the format in TrackCoordinates 128 | /* 129 | sint8 rotation_negative; // 0x00 130 | sint8 rotation_positive; // 0x01 131 | sint16 z_negative; // 0x02 132 | sint16 z_positive; // 0x04 133 | sint16 x; // 0x06 134 | sint16 y; // 0x08 135 | */ 136 | var configTable1Map = map[int]map[int]string{ 137 | 0: map[int]string{ 138 | 0: "TRACK_FLAT", 139 | 2: "TRACK_STATION_END", 140 | 7: "TRACK_VERTICAL_LOOP", 141 | 13: "TRACK_S_BEND", 142 | 17: "TRACK_TWIST", 143 | 18: "TRACK_HALF_LOOP", 144 | 19: "TRACK_CORKSCREW", 145 | 20: "TRACK_TOWER_BASE", 146 | 21: "TRACK_HELIX_SMALL", 147 | 22: "TRACK_HELIX_LARGE", 148 | 23: "TRACK_HELIX_LARGE_UNBANKED", 149 | 24: "TRACK_BRAKES", 150 | 26: "TRACK_ON_RIDE_PHOTO", 151 | 27: "TRACK_WATER_SPLASH", 152 | 29: "TRACK_BARREL_ROLL", 153 | 30: "TRACK_POWERED_LIFT", 154 | 31: "TRACK_HALF_LOOP_2", // ? 155 | 33: "TRACK_LOG_FLUME_REVERSER", 156 | 36: "TRACK_WHOA_BELLY", 157 | 43: "TRACK_LIFT_HILL", 158 | 46: "TRACK_SPINNING_TUNNEL", 159 | 47: "TRACK_ROTATION_CONTROL_TOGGLE", 160 | 52: "TRACK_RAPIDS", 161 | 152: "TRACK_WATERFALL", 162 | //152: "TRACK_WHIRLPOOL", 163 | 172: "TRACK_BRAKE_FOR_DROP", 164 | }, 165 | 1: map[int]string{ 166 | 0: "TRACK_NONE", 167 | 2: "TRACK_UP_25", 168 | 4: "TRACK_UP_60", 169 | 6: "TRACK_DOWN_25", 170 | 8: "TRACK_DOWN_60", 171 | 10: "TRACK_UP_90", 172 | 18: "TRACK_DOWN_90", 173 | }, 174 | 2: map[int]string{ 175 | 0: "TRACK_NONE", 176 | 2: "TRACK_UP_25", 177 | 4: "TRACK_UP_60", 178 | 6: "TRACK_DOWN_25", 179 | 8: "TRACK_DOWN_60", 180 | 10: "TRACK_UP_90", 181 | 18: "TRACK_DOWN_90", 182 | }, 183 | 3: map[int]string{ 184 | 0: "TRACK_BANK_NONE", 185 | 2: "TRACK_BANK_LEFT", 186 | 4: "TRACK_BANK_RIGHT", 187 | 15: "TRACK_BANK_UPSIDE_DOWN", 188 | }, 189 | 4: map[int]string{ 190 | 0: "TRACK_BANK_NONE", 191 | 2: "TRACK_BANK_LEFT", 192 | 4: "TRACK_BANK_RIGHT", 193 | 15: "TRACK_BANK_UPSIDE_DOWN", 194 | }, 195 | 5: map[int]string{ 196 | 0: "TRACK_NONE", 197 | 64: "TRACK_HALF_LOOP_UP", 198 | 192: "TRACK_HALF_LOOP_DOWN", 199 | 208: "TRACK_UNKNOWN_VERTICAL_LOOP", 200 | 224: "TRACK_CORKSCREW_DOWN", 201 | }, 202 | } 203 | -------------------------------------------------------------------------------- /exe_reader/track_values_test.go: -------------------------------------------------------------------------------- 1 | package exe_reader 2 | 3 | import "testing" 4 | 5 | var sidewaysTests = []struct { 6 | in int 7 | expected int 8 | }{ 9 | {0, 0}, 10 | {32, -2}, 11 | {64, -3}, 12 | {96, -4}, 13 | {160, 4}, 14 | {192, 3}, 15 | {224, 2}, 16 | } 17 | 18 | func TestSidewaysDelta(t *testing.T) { 19 | for _, tt := range sidewaysTests { 20 | out := SidewaysDelta(tt.in) 21 | if out != tt.expected { 22 | t.Errorf("expected SidewaysDelta(%d) to be %d, was %d", tt.in, tt.expected, out) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /genetic/files.go: -------------------------------------------------------------------------------- 1 | package genetic 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "os" 7 | "path/filepath" 8 | "time" 9 | ) 10 | 11 | // GetExperimentFiles returns a list of directories containing experiments 12 | func GetExperimentFiles() (folders []string, err error) { 13 | expDir := filepath.Join(*directory, "experiments") 14 | finfo, err := ioutil.ReadDir(expDir) 15 | if err != nil { 16 | return folders, err 17 | } 18 | for _, dir := range finfo { 19 | path := filepath.Join(expDir, dir.Name()) 20 | folders = append(folders, path) 21 | } 22 | return folders, nil 23 | } 24 | 25 | func GetOldFiles(d time.Duration) []string { 26 | var oldFolders []string 27 | files, err := GetExperimentFiles() 28 | if err != nil { 29 | panic(err) 30 | } 31 | now := time.Now().UTC() 32 | for _, dir := range files { 33 | metaPath := filepath.Join(dir, "meta.json") 34 | em, err := ReadMetadata(metaPath) 35 | if err != nil { 36 | panic(err) 37 | } 38 | duration := now.Sub(em.Date) 39 | if duration > d { 40 | oldFolders = append(oldFolders, dir) 41 | } 42 | } 43 | return oldFolders 44 | } 45 | 46 | // encode writes the given interface to the disk. 47 | func encode(name string, v interface{}) error { 48 | f, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 49 | if err != nil { 50 | return err 51 | } 52 | defer f.Close() 53 | enc := json.NewEncoder(f) 54 | return enc.Encode(v) 55 | } 56 | 57 | func ReadMetadata(filename string) (ExperimentMetadata, error) { 58 | f, err := os.Open(filename) 59 | if err != nil { 60 | panic(err) 61 | } 62 | var em ExperimentMetadata 63 | js := json.NewDecoder(f) 64 | err = js.Decode(&em) 65 | return em, err 66 | } 67 | -------------------------------------------------------------------------------- /genetic/genetic_test.go: -------------------------------------------------------------------------------- 1 | package genetic 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/kevinburke/rct/tracks" 7 | ) 8 | 9 | func TestSwap(t *testing.T) { 10 | a := &Member{ 11 | Track: []tracks.Element{ 12 | {Segment: tracks.TS_MAP[tracks.ELEM_FLAT]}, 13 | {Segment: tracks.TS_MAP[tracks.ELEM_25_DEG_UP]}, 14 | }, 15 | } 16 | b := &Member{ 17 | Track: []tracks.Element{ 18 | {Segment: tracks.TS_MAP[tracks.ELEM_25_DEG_DOWN]}, 19 | {Segment: tracks.TS_MAP[tracks.ELEM_60_DEG_DOWN]}, 20 | }, 21 | } 22 | c, d := Swap(a, b, 1, 1) 23 | cexpected := []tracks.Element{ 24 | {Segment: tracks.TS_MAP[tracks.ELEM_FLAT]}, 25 | {Segment: tracks.TS_MAP[tracks.ELEM_60_DEG_DOWN]}, 26 | } 27 | if c.Track[0] != cexpected[0] || c.Track[1] != cexpected[1] { 28 | t.Errorf("expected c to be %v, was %v", cexpected, c.Track) 29 | } 30 | 31 | dexpected := []tracks.Element{ 32 | {Segment: tracks.TS_MAP[tracks.ELEM_25_DEG_DOWN]}, 33 | {Segment: tracks.TS_MAP[tracks.ELEM_25_DEG_UP]}, 34 | } 35 | if d.Track[0] != dexpected[0] || d.Track[1] != dexpected[1] { 36 | t.Errorf("expected d to be %v, was %v", dexpected, d.Track) 37 | } 38 | } 39 | 40 | func elem(s tracks.SegmentType) tracks.Element { 41 | return tracks.Element{ 42 | Segment: tracks.TS_MAP[s], 43 | } 44 | } 45 | 46 | func TestCrossover(t *testing.T) { 47 | parent1 := &Member{ 48 | Track: []tracks.Element{ 49 | elem(tracks.ELEM_BEGIN_STATION), 50 | elem(tracks.ELEM_MIDDLE_STATION), 51 | elem(tracks.ELEM_MIDDLE_STATION), 52 | elem(tracks.ELEM_MIDDLE_STATION), 53 | elem(tracks.ELEM_END_STATION), 54 | elem(tracks.ELEM_FLAT_TO_RIGHT_BANK), 55 | elem(tracks.ELEM_RIGHT_QUARTER_TURN_3_TILES_BANK), 56 | elem(tracks.ELEM_RIGHT_QUARTER_TURN_3_TILES_BANK), 57 | elem(tracks.ELEM_RIGHT_QUARTER_BANKED_HELIX_LARGE_DOWN), 58 | elem(tracks.ELEM_RIGHT_BANK_TO_25_DEG_DOWN), 59 | elem(tracks.ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_DOWN), 60 | elem(tracks.ELEM_25_DEG_DOWN_TO_LEFT_BANK), 61 | elem(tracks.ELEM_LEFT_BANK_TO_25_DEG_UP), 62 | elem(tracks.ELEM_25_DEG_UP_TO_FLAT), 63 | elem(tracks.ELEM_FLAT_TO_25_DEG_UP), 64 | elem(tracks.ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_UP), 65 | }, 66 | } 67 | parent2 := &Member{ 68 | Track: []tracks.Element{ 69 | elem(tracks.ELEM_BEGIN_STATION), 70 | elem(tracks.ELEM_MIDDLE_STATION), 71 | elem(tracks.ELEM_MIDDLE_STATION), 72 | elem(tracks.ELEM_MIDDLE_STATION), 73 | elem(tracks.ELEM_END_STATION), 74 | elem(tracks.ELEM_S_BEND_RIGHT), 75 | elem(tracks.ELEM_LEFT_QUARTER_TURN_5_TILES), 76 | elem(tracks.ELEM_LEFT_QUARTER_TURN_3_TILES), 77 | elem(tracks.ELEM_FLAT_TO_RIGHT_BANK), 78 | elem(tracks.ELEM_RIGHT_BANK_TO_25_DEG_UP), 79 | elem(tracks.ELEM_25_DEG_UP_TO_LEFT_BANK), 80 | elem(tracks.ELEM_LEFT_BANK), 81 | elem(tracks.ELEM_LEFT_HALF_BANKED_HELIX_UP_SMALL), 82 | elem(tracks.ELEM_LEFT_HALF_BANKED_HELIX_DOWN_SMALL), 83 | elem(tracks.ELEM_LEFT_HALF_BANKED_HELIX_DOWN_LARGE), 84 | elem(tracks.ELEM_LEFT_HALF_BANKED_HELIX_UP_SMALL), 85 | }, 86 | } 87 | checkCompatible := func(track []tracks.Element) { 88 | for i, elem := range track[:len(track)-1] { 89 | if tracks.Compatible(elem, track[i+1]) == false { 90 | t.Errorf("Crossover generated incompatible segments: %s, %s", elem.Segment.String(), track[i+1].Segment.String()) 91 | } 92 | } 93 | } 94 | child1, child2 := crossoverOne(parent1, parent2, 11) 95 | checkCompatible(child1.Track) 96 | checkCompatible(child2.Track) 97 | 98 | // Reverse and try again. 99 | child1, child2 = crossoverOne(parent2, parent1, 10) 100 | checkCompatible(child1.Track) 101 | checkCompatible(child2.Track) 102 | } 103 | -------------------------------------------------------------------------------- /genetic/get_latest_track/main.go: -------------------------------------------------------------------------------- 1 | // Prints the latest track 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "path/filepath" 7 | "time" 8 | 9 | "github.com/kevinburke/rct/genetic" 10 | ) 11 | 12 | func main() { 13 | files, err := genetic.GetExperimentFiles() 14 | if err != nil { 15 | panic(err) 16 | } 17 | mostRecentFile := "" 18 | mostRecentDate := time.Date(2000, 1, 1, 1, 1, 1, 0, time.UTC) 19 | for _, dir := range files { 20 | metaPath := filepath.Join(dir, "meta.json") 21 | em, err := genetic.ReadMetadata(metaPath) 22 | if err != nil { 23 | panic(err) 24 | } 25 | if em.Date.Sub(mostRecentDate) > 0 { 26 | mostRecentDate = em.Date 27 | mostRecentFile = dir 28 | } 29 | } 30 | fmt.Println(mostRecentDate) 31 | fmt.Println(mostRecentFile) 32 | } 33 | -------------------------------------------------------------------------------- /genetic/get_old_experiments/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/kevinburke/rct/genetic" 8 | ) 9 | 10 | func main() { 11 | files := genetic.GetOldFiles(1 * time.Hour) 12 | for _, file := range files { 13 | fmt.Println(file) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /genetic/rct2.go: -------------------------------------------------------------------------------- 1 | // RCT2 specific track stuff goes in here 2 | package genetic 3 | 4 | import ( 5 | "log" 6 | "math" 7 | 8 | "github.com/kevinburke/rct/geo" 9 | "github.com/kevinburke/rct/physics" 10 | "github.com/kevinburke/rct/tracks" 11 | ) 12 | 13 | const STATION_LENGTH = 5 14 | const INITIAL_TRACK_LENGTH = 50 15 | 16 | // Create a station of length 10 17 | func CreateStation() []tracks.Element { 18 | station := make([]tracks.Element, STATION_LENGTH) 19 | beginning := tracks.TS_MAP[tracks.ELEM_BEGIN_STATION] 20 | middle := tracks.TS_MAP[tracks.ELEM_MIDDLE_STATION] 21 | end := tracks.TS_MAP[tracks.ELEM_END_STATION] 22 | station[0] = tracks.Element{Segment: beginning} 23 | for i := 1; i < STATION_LENGTH; i++ { 24 | station[i] = tracks.Element{Segment: middle} 25 | } 26 | station[STATION_LENGTH-1] = tracks.Element{Segment: end} 27 | return station 28 | } 29 | 30 | type scoreData struct { 31 | Collisions int 32 | Distance int 33 | NegativeSpeed int 34 | Length int 35 | } 36 | 37 | func intpow(x int64, y float64) int64 { 38 | val := math.Pow(float64(x), y) 39 | return int64(val) 40 | } 41 | 42 | func maxint(x, y int) int { 43 | if x > y { 44 | return x 45 | } 46 | return y 47 | } 48 | 49 | // GetScore determines how "good" a track is. Should take into account: 50 | // 51 | // - How close the track is to forming a complete loop 52 | // - How many collisions exist in the track 53 | // - Whether the car can make it all the way around the track 54 | // 55 | // As well as other components that mark a track as "interesting". 56 | func GetScore(t []tracks.Element) (int64, scoreData) { 57 | if len(t) == 0 { 58 | return 0, scoreData{} 59 | } 60 | vectors := geo.Vectors(t) 61 | stationStart := geo.Vector{geo.Point{0, 0, 0}, 0} 62 | trackEnd := vectors[len(t)-1] 63 | if trackEnd.Dir >= 350 { 64 | log.Panic("trackend is too high", trackEnd.Dir) 65 | } 66 | trackPieces := CompleteTrack(t[len(t)-1], trackEnd, stationStart) 67 | startingScore := int64(maxint(len(t), 70) * 300 * 1000) 68 | 69 | completedTrack := append(t, trackPieces...) 70 | 71 | data := &tracks.Data{ 72 | Elements: completedTrack, 73 | Clearance: 2, 74 | ClearanceDirection: tracks.CLEARANCE_ABOVE, 75 | } 76 | numCollisions := geo.NumCollisions(data) 77 | numNegativeSpeed := physics.NumNegativeSpeed(data) 78 | return startingScore - intpow(30000*int64(len(trackPieces)), 1.0001) - intpow(50000*int64(numCollisions), 1.007) - intpow(22000*int64(numNegativeSpeed), 1.0001), scoreData{ 79 | Collisions: numCollisions, 80 | Distance: len(trackPieces), 81 | NegativeSpeed: numNegativeSpeed, 82 | Length: len(t), 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /genetic/run_experiment/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "math/rand" 7 | "time" 8 | 9 | "github.com/kevinburke/rct/genetic" 10 | ) 11 | 12 | func init() { 13 | rand.Seed(time.Now().UTC().UnixNano()) 14 | } 15 | 16 | func main() { 17 | packageRoot := flag.String("package-root", "/", "Path to the folder running github.com/kevinburke/rct (for finding commit hashes)") 18 | flag.Parse() 19 | if len(flag.Args()) > 0 { 20 | log.Fatal("Usage: genetic [-directory DIRECTORY] ") 21 | } 22 | log.Fatal(genetic.Run(*packageRoot)) 23 | } 24 | -------------------------------------------------------------------------------- /geo/degree_test.go: -------------------------------------------------------------------------------- 1 | package geo 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSin(t *testing.T) { 8 | if sindeg(630) != -1 { 9 | t.Errorf("sin(630) should equal -1 but was %d", sindeg(630)) 10 | } 11 | if sindeg(0) != 0 { 12 | t.Errorf("sin(0) should equal 0 but was %d", sindeg(0)) 13 | } 14 | if sindeg(180) != 0 { 15 | t.Errorf("sin(180) should equal 0 but was %d", sindeg(180)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /geo/geo_test.go: -------------------------------------------------------------------------------- 1 | package geo 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/kevinburke/rct/tracks" 8 | ) 9 | 10 | func TestDiffFlat(t *testing.T) { 11 | seg := tracks.TS_MAP[tracks.ELEM_FLAT] 12 | out := Diff(seg, 0) 13 | expected := Point{1, 0, 0} 14 | if *out != expected { 15 | t.Errorf("expected straight line to be %#v, was %#v", expected, out) 16 | } 17 | } 18 | 19 | func TestDiffCurve(t *testing.T) { 20 | seg := tracks.TS_MAP[tracks.ELEM_RIGHT_QUARTER_TURN_5_TILES] 21 | out := Diff(seg, 0) 22 | expected := Point{3, -3, 0} 23 | if *out != expected { 24 | t.Errorf("expected curved 5-diameter track to be %#v, was %#v", expected, out) 25 | } 26 | } 27 | 28 | func TestDiffFlatRotated(t *testing.T) { 29 | seg := tracks.TS_MAP[tracks.ELEM_FLAT] 30 | out := Diff(seg, 90) 31 | expected := Point{0, 1, 0} 32 | if *out != expected { 33 | t.Errorf("expected flat track to be %#v, was %#v", expected, out) 34 | } 35 | 36 | out = Diff(seg, 180) 37 | expected = Point{-1, 0, 0} 38 | if *out != expected { 39 | t.Errorf("expected flat track to be %#v, was %#v", expected, out) 40 | } 41 | 42 | out = Diff(seg, 270) 43 | expected = Point{0, -1, 0} 44 | if *out != expected { 45 | t.Errorf("expected flat track to be %#v, was %#v", expected, out) 46 | } 47 | } 48 | 49 | var advanceTests = []struct { 50 | seg *tracks.Segment 51 | e int 52 | forward int 53 | sideways int 54 | direction tracks.DirectionDelta 55 | e_out int 56 | f_out int 57 | s_out int 58 | d_out tracks.DirectionDelta 59 | }{ 60 | {tracks.TS_MAP[tracks.ELEM_FLAT], 0, 0, 0, tracks.DIR_STRAIGHT, 0, 1, 0, tracks.DIR_STRAIGHT}, 61 | {tracks.TS_MAP[tracks.ELEM_FLAT], 0, 0, 0, tracks.DIR_90_DEG_RIGHT, 0, 0, -1, tracks.DIR_90_DEG_RIGHT}, 62 | {tracks.TS_MAP[tracks.ELEM_FLAT], 0, 0, 0, tracks.DIR_180_DEG, 0, -1, 0, tracks.DIR_180_DEG}, 63 | {tracks.TS_MAP[tracks.ELEM_60_DEG_UP], 0, 0, 0, tracks.DIR_STRAIGHT, 8, 1, 0, tracks.DIR_STRAIGHT}, 64 | {tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_5_TILES], 0, 0, 0, tracks.DIR_STRAIGHT, 0, 3, 3, tracks.DIR_90_DEG_LEFT}, 65 | {tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_5_TILES], 0, 0, 0, tracks.DIR_90_DEG_LEFT, 0, 3, -3, tracks.DIR_180_DEG}, 66 | {tracks.TS_MAP[tracks.ELEM_RIGHT_QUARTER_TURN_5_TILES], 0, 0, 0, tracks.DIR_STRAIGHT, 0, 3, -3, tracks.DIR_90_DEG_RIGHT}, 67 | } 68 | 69 | func TestAdvance(t *testing.T) { 70 | t.Skip("These tests don't work for turns...") 71 | for _, tt := range advanceTests { 72 | eout, fout, sout, dout := Advance(tt.seg, tt.e, tt.forward, tt.sideways, tt.direction) 73 | header := fmt.Sprintf("%s (init %d, %d, %d, %s):", tt.seg, tt.e, tt.forward, tt.sideways, tt.direction) 74 | if eout != tt.e_out { 75 | t.Errorf("%s expected %d elevation change, got %d", header, tt.e_out, eout) 76 | } 77 | if fout != tt.f_out { 78 | t.Errorf("%s expected %d forward delta, got %d", header, tt.f_out, fout) 79 | } 80 | if sout != tt.s_out { 81 | t.Errorf("%s expected %d sideways delta, got %d", header, tt.s_out, sout) 82 | } 83 | if dout != tt.d_out { 84 | t.Errorf("%s expected to go %s, got %d", header, tt.d_out, dout) 85 | } 86 | } 87 | } 88 | 89 | var advanceVectorTests = []struct { 90 | v Vector 91 | seg *tracks.Segment 92 | out Vector 93 | }{ 94 | { 95 | Vector{Point{0, 0, 0}, tracks.DIR_STRAIGHT}, 96 | tracks.TS_MAP[tracks.ELEM_FLAT], 97 | Vector{Point{1, 0, 0}, tracks.DIR_STRAIGHT}, 98 | }, 99 | { 100 | Vector{Point{0, 0, 0}, tracks.DIR_90_DEG_LEFT}, 101 | tracks.TS_MAP[tracks.ELEM_FLAT], 102 | Vector{Point{0, 1, 0}, tracks.DIR_90_DEG_LEFT}, 103 | }, 104 | { 105 | Vector{Point{0, 0, 0}, tracks.DIR_90_DEG_RIGHT}, 106 | tracks.TS_MAP[tracks.ELEM_FLAT], 107 | Vector{Point{0, -1, 0}, tracks.DIR_90_DEG_RIGHT}, 108 | }, 109 | { 110 | Vector{Point{0, 0, 0}, tracks.DIR_180_DEG}, 111 | tracks.TS_MAP[tracks.ELEM_FLAT], 112 | Vector{Point{-1, 0, 0}, tracks.DIR_180_DEG}, 113 | }, 114 | { 115 | Vector{Point{5, 7, 11}, tracks.DIR_STRAIGHT}, 116 | tracks.TS_MAP[tracks.ELEM_FLAT], 117 | Vector{Point{6, 7, 11}, tracks.DIR_STRAIGHT}, 118 | }, 119 | // simple up tests 120 | { 121 | Vector{Point{0, 0, 0}, tracks.DIR_STRAIGHT}, 122 | tracks.TS_MAP[tracks.ELEM_25_DEG_UP], 123 | Vector{Point{1, 0, 2}, tracks.DIR_STRAIGHT}, 124 | }, 125 | { 126 | Vector{Point{0, 0, 0}, tracks.DIR_STRAIGHT}, 127 | tracks.TS_MAP[tracks.ELEM_60_DEG_UP], 128 | Vector{Point{1, 0, 8}, tracks.DIR_STRAIGHT}, 129 | }, 130 | { 131 | Vector{Point{0, 0, 0}, tracks.DIR_90_DEG_LEFT}, 132 | tracks.TS_MAP[tracks.ELEM_25_DEG_UP], 133 | Vector{Point{0, 1, 2}, tracks.DIR_90_DEG_LEFT}, 134 | }, 135 | // down tests 136 | { 137 | Vector{Point{0, 0, 0}, tracks.DIR_STRAIGHT}, 138 | tracks.TS_MAP[tracks.ELEM_25_DEG_DOWN], 139 | Vector{Point{1, 0, -2}, tracks.DIR_STRAIGHT}, 140 | }, 141 | 142 | // left tests 143 | { 144 | Vector{Point{0, 0, 8}, tracks.DIR_STRAIGHT}, 145 | tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_5_TILES], 146 | Vector{Point{3, 3, 8}, tracks.DIR_90_DEG_LEFT}, 147 | }, 148 | { 149 | Vector{Point{0, 0, 8}, tracks.DIR_90_DEG_LEFT}, 150 | tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_5_TILES], 151 | Vector{Point{-3, 3, 8}, tracks.DIR_180_DEG}, 152 | }, 153 | { 154 | Vector{Point{0, 0, 8}, tracks.DIR_180_DEG}, 155 | tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_5_TILES], 156 | Vector{Point{-3, -3, 8}, tracks.DIR_90_DEG_RIGHT}, 157 | }, 158 | { 159 | Vector{Point{0, 0, 8}, tracks.DIR_90_DEG_RIGHT}, 160 | tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_5_TILES], 161 | Vector{Point{3, -3, 8}, tracks.DIR_STRAIGHT}, 162 | }, 163 | 164 | { 165 | Vector{Point{3, 4, 8}, tracks.DIR_STRAIGHT}, 166 | tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_5_TILES], 167 | Vector{Point{6, 7, 8}, tracks.DIR_90_DEG_LEFT}, 168 | }, 169 | 170 | // right tests 171 | { 172 | Vector{Point{0, 0, 8}, tracks.DIR_STRAIGHT}, 173 | tracks.TS_MAP[tracks.ELEM_RIGHT_QUARTER_TURN_5_TILES], 174 | Vector{Point{3, -3, 8}, tracks.DIR_90_DEG_RIGHT}, 175 | }, 176 | { 177 | Vector{Point{0, 0, 8}, tracks.DIR_90_DEG_LEFT}, 178 | tracks.TS_MAP[tracks.ELEM_RIGHT_QUARTER_TURN_5_TILES], 179 | Vector{Point{3, 3, 8}, tracks.DIR_STRAIGHT}, 180 | }, 181 | { 182 | Vector{Point{0, 0, 8}, tracks.DIR_180_DEG}, 183 | tracks.TS_MAP[tracks.ELEM_RIGHT_QUARTER_TURN_5_TILES], 184 | Vector{Point{-3, 3, 8}, tracks.DIR_90_DEG_LEFT}, 185 | }, 186 | { 187 | Vector{Point{0, 0, 8}, tracks.DIR_90_DEG_RIGHT}, 188 | tracks.TS_MAP[tracks.ELEM_RIGHT_QUARTER_TURN_5_TILES], 189 | Vector{Point{-3, -3, 8}, tracks.DIR_180_DEG}, 190 | }, 191 | 192 | { 193 | Vector{Point{3, 4, 8}, tracks.DIR_STRAIGHT}, 194 | tracks.TS_MAP[tracks.ELEM_RIGHT_QUARTER_TURN_5_TILES], 195 | Vector{Point{6, 1, 8}, tracks.DIR_90_DEG_RIGHT}, 196 | }, 197 | } 198 | 199 | func TestAdvanceVector(t *testing.T) { 200 | for _, tt := range advanceVectorTests { 201 | out := AdvanceVector(tt.v, tt.seg) 202 | helper := fmt.Sprintf("%s (%d, %d, %d) %v deg:", tt.seg, 203 | round(tt.v.Point[0]), round(tt.v.Point[1]), round(tt.v.Point[2]), tt.v.Dir) 204 | if out.Point != tt.out.Point { 205 | t.Errorf("%s expected point to be %v, was %v", helper, tt.out.Point, out.Point) 206 | } 207 | if out.Dir != tt.out.Dir { 208 | t.Errorf("%s expected direction to be %v, was %v", helper, tt.out.Dir, out.Dir) 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /geo/spatial_test.go: -------------------------------------------------------------------------------- 1 | package geo 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/kevinburke/rct/tracks" 7 | ) 8 | 9 | var stationTrack = &tracks.Data{ 10 | Elements: []tracks.Element{ 11 | tracks.Element{ 12 | Segment: tracks.TS_MAP[tracks.ELEM_END_STATION], 13 | }, 14 | }, 15 | } 16 | 17 | // The simplest possible circuit 18 | var completeTrack = &tracks.Data{ 19 | Elements: []tracks.Element{ 20 | tracks.Element{ 21 | Segment: tracks.TS_MAP[tracks.ELEM_END_STATION], 22 | }, 23 | tracks.Element{ 24 | Segment: tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_1_TILE], 25 | }, 26 | tracks.Element{ 27 | Segment: tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_1_TILE], 28 | }, 29 | tracks.Element{ 30 | Segment: tracks.TS_MAP[tracks.ELEM_FLAT], 31 | }, 32 | tracks.Element{ 33 | Segment: tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_1_TILE], 34 | }, 35 | tracks.Element{ 36 | Segment: tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_1_TILE], 37 | }, 38 | }, 39 | } 40 | 41 | var hillyTrack = &tracks.Data{ 42 | Elements: []tracks.Element{ 43 | tracks.Element{ 44 | Segment: tracks.TS_MAP[tracks.ELEM_END_STATION], 45 | }, 46 | tracks.Element{ 47 | Segment: tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_1_TILE], 48 | }, 49 | tracks.Element{ 50 | Segment: tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_1_TILE], 51 | }, 52 | tracks.Element{ 53 | Segment: tracks.TS_MAP[tracks.ELEM_FLAT_TO_25_DEG_UP], 54 | }, 55 | tracks.Element{ 56 | Segment: tracks.TS_MAP[tracks.ELEM_25_DEG_UP_TO_FLAT], 57 | }, 58 | tracks.Element{ 59 | Segment: tracks.TS_MAP[tracks.ELEM_FLAT_TO_25_DEG_DOWN], 60 | }, 61 | tracks.Element{ 62 | Segment: tracks.TS_MAP[tracks.ELEM_25_DEG_DOWN_TO_FLAT], 63 | }, 64 | tracks.Element{ 65 | Segment: tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_1_TILE], 66 | }, 67 | tracks.Element{ 68 | Segment: tracks.TS_MAP[tracks.ELEM_LEFT_QUARTER_TURN_1_TILE], 69 | }, 70 | tracks.Element{ 71 | Segment: tracks.TS_MAP[tracks.ELEM_FLAT], 72 | }, 73 | tracks.Element{ 74 | Segment: tracks.TS_MAP[tracks.ELEM_FLAT], 75 | }, 76 | tracks.Element{ 77 | Segment: tracks.TS_MAP[tracks.ELEM_FLAT], 78 | }, 79 | }, 80 | } 81 | 82 | func TestCircuit(t *testing.T) { 83 | td := &tracks.Data{} 84 | t.Skip("circuit checks broke recently") 85 | if IsCircuit(td) { 86 | t.Errorf("empty ride should not be a circuit.") 87 | } 88 | 89 | if IsCircuit(stationTrack) { 90 | t.Errorf("track with single station shouldn't be a circuit") 91 | } 92 | 93 | if !IsCircuit(completeTrack) { 94 | t.Errorf("Complete track should be marked as a circuit but wasn't") 95 | } 96 | 97 | if !IsCircuit(hillyTrack) { 98 | t.Errorf("Hilly track should be marked as a circuit but wasn't") 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /image/2d.go: -------------------------------------------------------------------------------- 1 | // Draw a RCT roller coaster in 2d. 2 | package image 3 | 4 | import ( 5 | "bufio" 6 | "fmt" 7 | "image" 8 | "image/color" 9 | "image/draw" 10 | "image/png" 11 | "log" 12 | "os" 13 | 14 | "github.com/kevinburke/rct/Godeps/_workspace/src/github.com/llgcode/draw2d/draw2dimg" 15 | "github.com/kevinburke/rct/td6" 16 | ) 17 | 18 | func SaveToPngFile(filePath string, m image.Image) { 19 | f, err := os.Create(filePath) 20 | if err != nil { 21 | log.Println(err) 22 | os.Exit(1) 23 | } 24 | defer f.Close() 25 | b := bufio.NewWriter(f) 26 | err = png.Encode(b, m) 27 | if err != nil { 28 | log.Println(err) 29 | os.Exit(1) 30 | } 31 | err = b.Flush() 32 | if err != nil { 33 | log.Println(err) 34 | os.Exit(1) 35 | } 36 | fmt.Printf("Wrote %s OK.\n", filePath) 37 | } 38 | 39 | const PIECE_WIDTH = 8 40 | const IMG_HEIGHT = 400 41 | 42 | var RED = color.RGBA{0xff, 0x00, 0x00, 0xff} 43 | var BLUE = color.RGBA{0x00, 0x00, 0xff, 0xff} 44 | 45 | func Draw(r *td6.Ride) image.Image { 46 | width := PIECE_WIDTH * (len(r.TrackData.Elements) + 2) 47 | rect := image.Rect(0, 0, width, IMG_HEIGHT) 48 | i := image.NewRGBA(rect) 49 | c := color.RGBA{0xff, 0xff, 0xff, 0xff} 50 | draw.Draw(i, i.Bounds(), &image.Uniform{c}, image.ZP, draw.Src) 51 | gc := draw2dimg.NewGraphicContext(i) 52 | x := float64(PIECE_WIDTH) 53 | y := float64(IMG_HEIGHT) - 20.0 54 | for j := 0; j < len(r.TrackData.Elements); j++ { 55 | gc.MoveTo(x, y) 56 | elem := r.TrackData.Elements[j] 57 | seg := elem.Segment 58 | if elem.ChainLift { 59 | gc.SetStrokeColor(BLUE) 60 | } else { 61 | gc.SetStrokeColor(RED) 62 | } 63 | y -= 8.0 * float64(seg.ElevationDelta) 64 | gc.LineTo(float64(x+PIECE_WIDTH), y) 65 | gc.Stroke() 66 | x += PIECE_WIDTH 67 | } 68 | return i 69 | } 70 | 71 | func main() { 72 | r := td6.ReadRide("/Users/kevin/code/go/src/github.com/kevinburke/rct/rides/mischief.td6") 73 | img := Draw(r) 74 | SaveToPngFile("test.png", img) 75 | } 76 | -------------------------------------------------------------------------------- /image/above.go: -------------------------------------------------------------------------------- 1 | // Draw a RCT roller coaster from above. 2 | package image 3 | 4 | import ( 5 | "fmt" 6 | "image" 7 | "image/color" 8 | "image/draw" 9 | 10 | "github.com/kevinburke/rct/td6" 11 | ) 12 | 13 | func getWhiteRectangle(width int, height int) *image.RGBA { 14 | rect := image.Rect(0, 0, 600, 600) 15 | i := image.NewRGBA(rect) 16 | c := color.RGBA{0xff, 0xff, 0xff, 0xff} 17 | draw.Draw(i, i.Bounds(), &image.Uniform{c}, image.ZP, draw.Src) 18 | return i 19 | } 20 | 21 | func DrawAbove(r *td6.Ride) image.Image { 22 | i := getWhiteRectangle(600, 600) 23 | x := 300.0 24 | y := 300.0 25 | fmt.Println(x) 26 | fmt.Println(y) 27 | for j := 0; j < len(r.TrackData.Elements); j++ { 28 | 29 | } 30 | //width := PIECE_WIDTH * (len(r.TrackData.Elements) + 2) 31 | //rect := image.Rect(0, 0, width, IMG_HEIGHT) 32 | //i := image.NewRGBA(rect) 33 | //c := color.RGBA{0xff, 0xff, 0xff, 0xff} 34 | //draw.Draw(i, i.Bounds(), &image.Uniform{c}, image.ZP, draw.Src) 35 | //gc := draw2d.NewGraphicContext(i) 36 | //x := float64(PIECE_WIDTH) 37 | //y := float64(IMG_HEIGHT) - 20.0 38 | //for j := 0; j < len(r.TrackData.Elements); j++ { 39 | //gc.MoveTo(x, y) 40 | //elem := r.TrackData.Elements[j] 41 | //seg := elem.Segment 42 | //if elem.ChainLift { 43 | //gc.SetStrokeColor(BLUE) 44 | //} else { 45 | //gc.SetStrokeColor(RED) 46 | //} 47 | //y -= 8.0 * float64(seg.ElevationDelta) 48 | //gc.LineTo(float64(x+PIECE_WIDTH), y) 49 | //gc.Stroke() 50 | //x += PIECE_WIDTH 51 | //} 52 | //return i 53 | 54 | // not correct. 55 | return i 56 | } 57 | -------------------------------------------------------------------------------- /image/above_runner/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/kevinburke/rct/td6" 4 | import "github.com/kevinburke/rct/image" 5 | 6 | func main() { 7 | r := td6.ReadRide("/Users/kevin/code/go/src/github.com/kevinburke/rct/rides/mischief.td6") 8 | img := image.Draw(r) 9 | image.SaveToPngFile("test.png", img) 10 | } 11 | -------------------------------------------------------------------------------- /interface.md: -------------------------------------------------------------------------------- 1 | user needs to define: 2 | 3 | - a fitness function which takes a Member and returns a score 4 | - pool size 5 | - mutation rate 6 | - crossover rate 7 | - a Crossover function which takes two Members and swaps them 8 | 9 | Punting on this for now, will build it later, hopefully 10 | -------------------------------------------------------------------------------- /physics/physics.go: -------------------------------------------------------------------------------- 1 | package physics 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/kevinburke/rct/tracks" 7 | ) 8 | 9 | func max(a float64, b float64) float64 { 10 | if a > b { 11 | return a 12 | } else { 13 | return b 14 | } 15 | } 16 | 17 | const CHAIN_SPEED_MPH = 6 18 | 19 | // Map of elevation changes to speed changes. Dumbest possible thing is to make 20 | // speed a function of the change in elevation. 21 | // 22 | // result values are in change in MPH. Came from observing in-game behavior 23 | // with 2-car mine trains. 24 | var accelerationMap = map[int]float64{ 25 | 8: 7, 26 | // hack: geometric average between 1.4 and 7 27 | 4: 2.37, 28 | 29 | 2: 1.4, 30 | 1: 0.7, 31 | 0: -0.1, 32 | 33 | // negative values have slightly higher friction coefficients. the game 34 | // does something fancy I think to mitigate this/ make it always possible 35 | // to climb within 2 heights of your last height. instead, do the dumb 36 | // thing 37 | -1: -0.75, 38 | -2: -1.45, 39 | -4: -2.40, 40 | -8: -7, 41 | } 42 | 43 | func NewSpeed(currentSpeed float64, e tracks.Element) float64 { 44 | if e.ChainLift == true || e.Station == true { 45 | return max(currentSpeed, CHAIN_SPEED_MPH) 46 | } 47 | accel, ok := accelerationMap[e.Segment.ElevationDelta] 48 | if !ok { 49 | log.Panicf("no acceleration available for element: %#v", e) 50 | } 51 | // XXX handle brakes; currently blacklisted in segmentfns 52 | return currentSpeed + accel 53 | } 54 | 55 | // NumNegativeSpeed returns the number of times the car speed turns negative 56 | func NumNegativeSpeed(t *tracks.Data) int { 57 | speed := float64(6) 58 | numNegativeSpeed := 0 59 | for i := range t.Elements { 60 | e := t.Elements[i] 61 | speed = NewSpeed(speed, e) 62 | if speed < 0 { 63 | numNegativeSpeed++ 64 | // Reset the speed so the car keeps going forward. 65 | speed = float64(6) 66 | } 67 | } 68 | return numNegativeSpeed 69 | } 70 | -------------------------------------------------------------------------------- /profile/1.profile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/profile/1.profile -------------------------------------------------------------------------------- /profile/2.profile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/profile/2.profile -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # What is it? 2 | 3 | The goal of the project is to generate cool looking roller coasters via a 4 | genetic algorithm. Something like this: 5 | 6 | The coolest coaster ever 9 | 10 | ## What's been done so far? 11 | 12 | Here's what we have: 13 | 14 | - A list of all of the track pieces used in the game and metadata about them 15 | (elevation change, left/right etc) 16 | 17 | - A way to read rides from the game, and serialize track data generated outside 18 | the game to be used in the game. 19 | 20 | - Some library functions to make vector math easier (e.g. after this track 21 | piece, what is the new location and orientation of the car?) 22 | 23 | - A basic genetic algorithm with mutation, selection, crossover, and a fitness 24 | function. 25 | 26 | - Results of every experiment are recorded to disk. There's a server you can 27 | use to view the results of experiments, and which has an easy "convert to TD6" 28 | button. 29 | 30 | ## What needs to get done? 31 | 32 | - Tracks currently don't load in the game because the collision detection 33 | algorithm isn't correct. The experiment runner will report 0 collisions, 34 | but tracks will fail to load because there is actually a collision. You can 35 | verify this by printing out the individual track pieces, starting the game and 36 | attempting to rebuild the track. I'm not sure how best to fix this than by 37 | attempting to fix individual instances as they pop up. 38 | 39 | - [The fitness function][fitness] does not have enough inputs. Specifically, it 40 | currently checks whether a track is a complete loop and doesn't collide with 41 | itself. It *could* check: 42 | 43 | - Excitement. The easiest heuristic for excitement is [what's 44 | used to compute excitement in the game Roller Coaster Tycoon 45 | 2][openrct2-excitement]; would take into account the number of drops, the 46 | speed, G forces etc. 47 | 48 | - Physics - whether the car can make it around the track. 49 | 50 | - G forces 51 | 52 | - More ! Scenery, etc, you name it. 53 | 54 | - The fitness function is currently **very slow**. Need to run the code with 55 | `pprof` to figure out what exactly is slow, and then improve its performance. 56 | The existing code is extremely dumb - it runs sequentially in one thread. 57 | Likely there are some easy improvements to be made by using more than one CPU 58 | at a time. 59 | 60 | - The server can display track data but the camera is out of position, so 61 | sometimes the track is not visible. It would be really neat to be able to 62 | better see track data outside of the game. 63 | 64 | - The genetic algorithm will probably need some tweaking once the fitness 65 | function has more parameters. This is hard to evaluate right now. 66 | 67 | [fitness]: https://github.com/kevinburke/rct/blob/master/genetic/rct2.go#L436 68 | [openrct2-excitement]: https://github.com/OpenRCT2/OpenRCT2/blob/develop/src/ride/ride_ratings.c#L2106 69 | 70 | ## Installation 71 | 72 | You need to have Go installed (I use Go 1.5, but it should work with any 73 | version). Once you do that, run: 74 | 75 | ```bash 76 | go get github.com/kevinburke/rct 77 | ``` 78 | 79 | Once you've downloaded the source code, run `make install` to ensure all third 80 | party dependencies are downloaded. 81 | 82 | Check you have a valid install by running: `make test`. You should get output 83 | like this: 84 | 85 | ```bash 86 | go test -timeout 1s \ 87 | ./bits/... \ 88 | ./genetic/... \ 89 | ./geo/... \ 90 | ./image/... \ 91 | ./rle/... \ 92 | ./server/... \ 93 | ./td6/... \ 94 | ./tracks/... 95 | ? github.com/kevinburke/rct/bits [no test files] 96 | ok github.com/kevinburke/rct/genetic 0.011s 97 | ? github.com/kevinburke/rct/genetic/get_latest_track [no test files] 98 | ? github.com/kevinburke/rct/genetic/get_old_experiments [no test files] 99 | ? github.com/kevinburke/rct/genetic/run_experiment [no test files] 100 | ok github.com/kevinburke/rct/geo 0.010s 101 | ? github.com/kevinburke/rct/image [no test files] 102 | ? github.com/kevinburke/rct/image/above_runner [no test files] 103 | ok github.com/kevinburke/rct/rle 0.011s 104 | ? github.com/kevinburke/rct/rle/decode_td6 [no test files] 105 | ? github.com/kevinburke/rct/rle/encode_td6 [no test files] 106 | ? github.com/kevinburke/rct/server [no test files] 107 | ok github.com/kevinburke/rct/td6 0.010s 108 | ok github.com/kevinburke/rct/tracks 0.008s 109 | ? github.com/kevinburke/rct/tracks/branch_factor [no test files] 110 | ``` 111 | 112 | (Some of the packages don't have tests unfortunately) 113 | 114 | ## Running experiments 115 | 116 | You should be able to run new experiments by typing `make experiment`. This 117 | will place new experiments in subdirectories in `/usr/local/rct`. It should 118 | also give output in the console that looks like this: 119 | 120 | ``` 121 | run_experiment --package-root ~/code/go/src/github.com/kevinburke/rct 122 | Experiment exp_ddc75161-8370-4ead-aa76-71ebdb59f1d5 123 | ====================================== 124 | Iteration 0: 500 members, best member iter_d667e484-ef33-40cd-ad30-52d5e6a90036 has score 1680000, median 1527000, worst has score 172000 125 | Iteration 1: 500 members, best member iter_d667e484-ef33-40cd-ad30-52d5e6a90036 has score 1680000, median 1517000, worst has score 172000 126 | Iteration 2: 500 members, best member iter_7f34d78b-4e1e-4be2-b17f-d46b51fa065d has score 1672000, median 1443000, worst has score 172000 127 | Iteration 3: 500 members, best member iter_9cbedb01-9447-40b0-9973-aeb4af67faf5 has score 1672000, median 1443000, worst has score 172000 128 | Iteration 4: 500 members, best member iter_9cbedb01-9447-40b0-9973-aeb4af67faf5 has score 1672000, median 1554000, worst has score 172000 129 | ``` 130 | 131 | Start the server by running `make serve` and you should be able to view your 132 | experiment by browsing at `localhost:8080`. 133 | 134 | ### Package Layout 135 | 136 | There are a few different packages in here. 137 | 138 | - `genetic` - implementation of a genetic algorithm to build coasters. 139 | Hopefully this can become a better implementation than just RCT specific but 140 | there are other genetic algorithm implementations for Go at the moment, so 141 | this is not a huge priority. 142 | 143 | - `exe_reader` - Reads the RCT2 exe and prints out metadata about track 144 | segments. 145 | 146 | - `rle` - deals with run length encoding of RCT files (so they can be 147 | read/written in the game). More info in the specific package. 148 | 149 | - `td6` - a td6 file is a RCT ride file. This package encodes/decodes raw TD6 150 | file data into Go structs. 151 | 152 | - `image/*` - turns track data into pretty PNG files. Hasn't been touched in 153 | a while and only ever implemented a 2d viewer. Decided to try using three.js 154 | instead, for greater compatibility and access to a better feature set. 155 | 156 | - `bits` - convenience functions for dealing with bits. 157 | 158 | - `wip/*` - various "discovery" scripts that read bytes from `openrct2.exe` 159 | dealing with track data. Shouldn't need to touch this, we might need to clean 160 | it up at some point because Go complains about the number of `main` files in 161 | this folder. 162 | 163 | ## Donating 164 | 165 | Donations free up time to make improvements to the project, and respond to 166 | bug reports. You can send donations via Paypal's "Send Money" feature to 167 | kev@inburke.com. Donations are not tax deductible in the USA. 168 | -------------------------------------------------------------------------------- /ride-rating-from-c/a.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/ride-rating-from-c/a.out -------------------------------------------------------------------------------- /ride-rating-from-c/compiling.md: -------------------------------------------------------------------------------- 1 | This will document my attempts to compile the OpenRCT2 project from outside of 2 | the project, so I can call functions in it from the outside. 3 | 4 | ## Compiling with GCC 5 | 6 | When attempting to link `OpenRCT2/src/ride/ride_ratings.c`, you get two errors: 7 | 8 | - unknown use of instruction mnemonic without a size suffix 9 | - error: brackets expression not supported on this target 10 | 11 | Both of these sound like either the source needs to be changed or GCC does not 12 | support compiling inline ASM yet. 13 | 14 | ### Update 15 | 16 | Surprise! GCC is not actually GCC, it's a linked version of Clang. Downloading 17 | GCC now. 18 | 19 | The actual GCC 4.9 fails because of a `error: -masm=intel not supported in this 20 | configuration` error. Apparently the Mac assembler can't support Intel syntax. 21 | 22 | ## Compiling with Clang 23 | 24 | I tried 25 | 26 | ``` 27 | clang -m32 -mllvm --x86-asm-syntax=intel -std=gnu99 get_rating.c 28 | ``` 29 | 30 | This failed with the same errors as above. 31 | 32 | ## Rewriting the syntax 33 | 34 | The next suggestion was to add a GAS-compatible version of the assembler in 35 | addresses.h. I might do this, and/or maintain a fork that doesn't ever call 36 | RCT_CALLPROC/CALLFUNC. 37 | -------------------------------------------------------------------------------- /ride-rating-from-c/get_rating.c: -------------------------------------------------------------------------------- 1 | #include "/Users/kevin/code/OpenRCT2/src/ride/ride_ratings.c" 2 | #include 3 | 4 | int main(void) 5 | { 6 | ride_ratings_update_state_0(); 7 | } 8 | -------------------------------------------------------------------------------- /rideheights.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/rideheights.jpg -------------------------------------------------------------------------------- /ridenames.go: -------------------------------------------------------------------------------- 1 | package rct 2 | 3 | // Rides in the order they're used in the game 4 | var RIDENAMES = []string{ 5 | "Spiral Roller coaster", 6 | "Stand Up Coaster", 7 | "Suspended Swinging", 8 | "Inverted", 9 | "Steel Mini Coaster", 10 | "Mini Railroad", 11 | "Monorail", 12 | "Mini Suspended Coaster", 13 | "Bumper Boats", 14 | "Wooden Wild Mine/Mouse", 15 | "Steeplechase/Motorbike/Soap Box Derby", 16 | "Car Ride", 17 | "Launched Freefall", 18 | "Bobsleigh Coaster", 19 | "Observation Tower", 20 | "Looping Roller Coaster", 21 | "Dinghy Slide", 22 | "Mine Train Coaster", 23 | "Chairlift", 24 | "Corkscrew Roller Coaster", 25 | "Maze", 26 | "Spiral Slide", 27 | "Go Karts", 28 | "Log Flume", 29 | "River Rapids", 30 | "Bumper Cars", 31 | "Pirate Ship", 32 | "Swinging Inverter Ship", 33 | "Food Stall", 34 | "(none)", 35 | "Drink Stall", 36 | "(none)", 37 | "Shop (all types)", 38 | "Merry Go Round", 39 | "Balloon Stall (maybe)", 40 | "Information Kiosk", 41 | "Bathroom", 42 | "Ferris Wheel", 43 | "Motion Simulator", 44 | "3D Cinema", 45 | "Gravitron", 46 | "Space Rings", 47 | "Reverse Freefall Coaster", 48 | "Elevator", 49 | "Vertical Drop Roller Coaster", 50 | "ATM", 51 | "Twist", 52 | "Haunted House", 53 | "First Aid", 54 | "Circus Show", 55 | "Ghost Train", 56 | "Twister Roller Coaster", 57 | "Wooden Roller Coaster", 58 | "Side-Friction Roller Coaster", 59 | "Wild Mouse", 60 | "Multi Dimension Coaster", 61 | "(none)", 62 | "Flying Roller Coaster", 63 | "(none)", 64 | "Virginia Reel", 65 | "Splash Boats", 66 | "Mini Helicopters", 67 | "Lay-down Roller Coaster", 68 | "Suspended Monorail", 69 | "(none)", 70 | "Reverser Roller Coaster", 71 | "Heartline Twister Roller Coaster", 72 | "Mini Golf", 73 | "Giga Coaster", 74 | "Roto-Drop", 75 | "Flying Saucers", 76 | "Crooked House", 77 | "Monorail Cycles", 78 | "Compact Inverted Coaster", 79 | "Water Coaster", 80 | "Air Powered Vertical Coaster", 81 | "Inverted Hairpin Coaster", 82 | "Magic Carpet", 83 | "Submarine Ride", 84 | "River Rafts", 85 | "(none)", 86 | "Enterprise", 87 | "(none)", 88 | "(none)", 89 | "(none)", 90 | "(none)", 91 | "Inverted Impulse Coaster", 92 | "Mini Roller Coaster", 93 | "Mine Ride", 94 | "LIM Launched Roller Coaster", 95 | } 96 | -------------------------------------------------------------------------------- /rides/blackwidow.td6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/rides/blackwidow.td6 -------------------------------------------------------------------------------- /rides/generated_mine_train.go: -------------------------------------------------------------------------------- 1 | package rides 2 | 3 | import "github.com/kevinburke/rct/td6" 4 | 5 | var ride td6.Ride 6 | 7 | func init() { 8 | //ride = &td6.Ride{ 9 | //RideType: 17, 10 | //VehicleColorScheme: 0, 11 | //XSpaceRequired: 32, 12 | //YSpaceRequired: 36, 13 | //OperatingMode: 1, 14 | //ControlFlags: (*td6.ControlFlags)(0xc20802b0e0), 15 | //NumTrains: 0x2, 16 | //CarsPerTrain: 0x6, 17 | //MinWaitTime: 0x0, 18 | //MaxWaitTime: 0x0, 19 | //HasLoop: false, 20 | //SteepLiftChain: false, 21 | //CurvedLiftChain: false, 22 | //Banking: false, 23 | //SteepSlope: false, 24 | //FlatToSteep: false, 25 | //SlopedCurves: false, 26 | //SteepTwist: false, 27 | //SBends: false, 28 | //SmallRadiusCurves: false, 29 | //SmallRadiusBanked: false, 30 | //NumInversions: 0x0, 31 | //MaxSpeed: 0x0, 32 | //AverageSpeed: 0x0, 33 | //VehicleType: "AMT1 ", 34 | //DatData: []uint8(nil), 35 | //Egresses: []*td6.Egress(nil), 36 | //Excitement: 0, 37 | //Intensity: 0, 38 | //Nausea: 0} 39 | } 40 | -------------------------------------------------------------------------------- /rides/hello.td4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/rides/hello.td4 -------------------------------------------------------------------------------- /rides/mine-train-gold-rush.roundtrip-thru-go.TD6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/rides/mine-train-gold-rush.roundtrip-thru-go.TD6 -------------------------------------------------------------------------------- /rides/mine-train-gold-rush.roundtrip.td6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/rides/mine-train-gold-rush.roundtrip.td6 -------------------------------------------------------------------------------- /rides/mine-train-gold-rush.td6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/rides/mine-train-gold-rush.td6 -------------------------------------------------------------------------------- /rides/mine-train-gold-rush.td6.decoded: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/rides/mine-train-gold-rush.td6.decoded -------------------------------------------------------------------------------- /rides/mine_train.go: -------------------------------------------------------------------------------- 1 | package rides 2 | 3 | import "github.com/kevinburke/rct/td6" 4 | 5 | var mineride *td6.Ride 6 | 7 | func init() { 8 | 9 | //mineride = &td6.Ride{ 10 | //RideType: 17, 11 | //VehicleColorScheme: 0, 12 | //XSpaceRequired: 10, 13 | //YSpaceRequired: 20, 14 | //OperatingMode: 34, 15 | //ControlFlags: (*td6.ControlFlags)(0x2081d62f0), 16 | //NumTrains: 0x2, 17 | //CarsPerTrain: 0x6, 18 | //MinWaitTime: 0x8, 19 | //MaxWaitTime: 0x3c, 20 | //HasLoop: false, 21 | //SteepLiftChain: false, 22 | //CurvedLiftChain: false, 23 | //Banking: false, 24 | //SteepSlope: false, 25 | //FlatToSteep: false, 26 | //SlopedCurves: false, 27 | //SteepTwist: false, 28 | //SBends: false, 29 | //SmallRadiusCurves: false, 30 | //SmallRadiusBanked: false, 31 | //NumInversions: 0x0, 32 | //MaxSpeed: 0x11, 33 | //AverageSpeed: 0x7, 34 | //VehicleType: "AMT1 ", 35 | //DatData: []uint8{0x80, 36 | //0x46, 37 | //0xc1, 38 | //0x8, 39 | //0x41, 40 | //0x4d, 41 | //0x54, 42 | //0x31, 43 | //0x20, 44 | //0x20, 45 | //0x20, 46 | //0x20, 47 | //0xad, 48 | //0x74, 49 | //0xec, 50 | //0xe8}, 51 | //Egresses: []*td6.Egress{(*td6.Egress)(0x2081ea020), 52 | //(*td6.Egress)(0x2081ea040)}, 53 | //Excitement: 61, 54 | //Intensity: 70, 55 | //Nausea: 47} 56 | } 57 | -------------------------------------------------------------------------------- /rides/mischief.td6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/rides/mischief.td6 -------------------------------------------------------------------------------- /rides/mischief.td6.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/rides/mischief.td6.out -------------------------------------------------------------------------------- /rides/raptor.td4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/rides/raptor.td4 -------------------------------------------------------------------------------- /rides/woodchip.td6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/rides/woodchip.td6 -------------------------------------------------------------------------------- /rle/decode_td6/decode_td6.go: -------------------------------------------------------------------------------- 1 | // Decode a TD6 file and print the results to stdout. It's going to be a blob 2 | // of binary data, you probably want to redirect it to a file. 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "flag" 8 | "io" 9 | "log" 10 | "os" 11 | 12 | "github.com/kevinburke/rct/rle" 13 | ) 14 | 15 | func main() { 16 | filename := flag.String("filename", "", "td6 file to decode") 17 | flag.Parse() 18 | if *filename == "" { 19 | log.Fatal("please enter a filename") 20 | } 21 | f, err := os.Open(*filename) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | reader := rle.NewReader(f) 26 | // xxx is there a better way to do this? 27 | rr := io.TeeReader(reader, os.Stdout) 28 | var bitbuffer bytes.Buffer 29 | bitbuffer.ReadFrom(rr) 30 | } 31 | -------------------------------------------------------------------------------- /rle/encode_td6/encode_td6.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | 9 | "github.com/kevinburke/rct/rle" 10 | ) 11 | 12 | func main() { 13 | usage := "Invalid arguments. Usage: encode_td6 filename" 14 | flag.Parse() 15 | args := flag.Args() 16 | if len(args) != 1 { 17 | log.Fatal(usage) 18 | } 19 | filename := args[0] 20 | bits, err := ioutil.ReadFile(filename) 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | writer := rle.NewWriter(os.Stdout) 25 | writer.Write(bits) 26 | } 27 | -------------------------------------------------------------------------------- /rle/lib.go: -------------------------------------------------------------------------------- 1 | // Dealing with RCT's run length encoding 2 | 3 | // This should follow the API laid out by the gzip package. 4 | // Author: Kevin Burke 5 | package rle 6 | 7 | import ( 8 | "bufio" 9 | "bytes" 10 | "encoding/binary" 11 | "io" 12 | ) 13 | 14 | type Reader struct { 15 | r *bufio.Reader 16 | off int 17 | } 18 | 19 | type Writer struct { 20 | w io.Writer 21 | closed bool 22 | err error 23 | } 24 | 25 | func NewWriter(w io.Writer) *Writer { 26 | return &Writer{w: w} 27 | } 28 | 29 | // Create a new reader that can read encoded files. 30 | func NewReader(r io.Reader) *Reader { 31 | return &Reader{ 32 | r: bufio.NewReader(r), 33 | off: 0, 34 | } 35 | } 36 | 37 | func min(a int, b int) int { 38 | if a < b { 39 | return a 40 | } else { 41 | return b 42 | } 43 | } 44 | 45 | // Compute the checksum for a given series of bytes. 46 | func checksum(ride []byte) uint32 { 47 | 48 | summation := uint32(0) 49 | for i := range ride { 50 | tmp := summation + uint32(ride[i]) 51 | summation = (summation & 0xffffff00) | (tmp & 0x000000ff) 52 | highthree := summation & (7 << 29) 53 | summation = summation << 3 54 | summation |= highthree >> 29 55 | } 56 | summation -= 120001 57 | return summation 58 | } 59 | 60 | // Read the encoded file into the byte array, decoding it in the process. 61 | // Note, a TD6 file will have a checksum appended on the end. This checksum is 62 | // not removed by the Read function. 63 | func (z *Reader) Read(ride []byte) (int, error) { 64 | var n int 65 | if len(ride) == 0 { 66 | return 0, nil 67 | } 68 | // Read one byte 69 | c, err := z.r.ReadByte() 70 | if err != nil { 71 | return 0, err 72 | } 73 | // if positive, read that number of bytes 74 | if c >= 0 && c <= 128 { 75 | // copy c bytes from the reader to the byte array 76 | n, err = z.r.Read(ride[:min(int(c)+1, len(ride))]) 77 | if err != nil { 78 | return 0, err 79 | } 80 | z.off += int(n) 81 | 82 | } else { 83 | // if negative, read the next byte, repeat that byte (-c + 1) times 84 | repeatByte, err := z.r.ReadByte() 85 | repeatTimes := 0 - int8(c) + 1 86 | rpts := bytes.Repeat([]byte{repeatByte}, int(repeatTimes)) 87 | repeatReader := bytes.NewReader(rpts) 88 | n, err = repeatReader.Read(ride[:int(repeatTimes)]) 89 | if err != nil { 90 | return 0, err 91 | } 92 | } 93 | return n, nil 94 | } 95 | 96 | // implement the writing algorithm. Need to return the byte array so that we 97 | // can compute checksums. 98 | func (z *Writer) write(ride []byte) ([]byte, int, error) { 99 | if len(ride) == 0 { 100 | return []byte{}, 0, nil 101 | } 102 | if len(ride) == 1 { 103 | towrite := []byte{byte(1), ride[0]} 104 | n, err := z.w.Write(towrite) 105 | if err != nil { 106 | return []byte{}, n, err 107 | } 108 | return towrite, 1, nil 109 | } 110 | // if you find an immediate duplicate, read all of the duplicates to get 111 | // the count. write the count, then write the duplicate byte. 112 | if len(ride) > 1 && ride[0] == ride[1] { 113 | count := 2 114 | for { 115 | if len(ride) > count && ride[count-1] == ride[count] && count <= 127 { 116 | count += 1 117 | } else { 118 | break 119 | } 120 | } 121 | 122 | inv := -count + 1 123 | towrite := []byte{byte(inv), ride[0]} 124 | n, err := z.w.Write(towrite) 125 | if err != nil { 126 | return []byte{}, n, err 127 | } 128 | return towrite, count, nil 129 | } else { 130 | // read bytes until you find ones that are the same 131 | count := 1 132 | for { 133 | if len(ride)-1 == count { 134 | count += 1 135 | break 136 | } 137 | if ride[count] != ride[count+1] && count <= 127 { 138 | count += 1 139 | } else { 140 | break 141 | } 142 | } 143 | towrite := append([]byte{byte(len(ride[0:count]) - 1)}, ride[0:count]...) 144 | n, err := z.w.Write(towrite) 145 | if err != nil { 146 | return []byte{}, n, err 147 | } 148 | return towrite, count, nil 149 | } 150 | } 151 | 152 | func (z *Writer) Write(ride []byte) (int, error) { 153 | count := 0 154 | var b bytes.Buffer 155 | for { 156 | encoded, n, err := z.write(ride[count:]) 157 | if err != nil { 158 | return n, err 159 | } 160 | b.Write(encoded) 161 | if count >= len(ride) { 162 | // all done. write checksum and continue 163 | cks := checksum(b.Bytes()) 164 | buf := new(bytes.Buffer) 165 | err := binary.Write(buf, binary.LittleEndian, cks) 166 | if err != nil { 167 | return n, err 168 | } 169 | n, err := z.w.Write(buf.Bytes()) 170 | if err != nil { 171 | return n, err 172 | } 173 | return count + n, nil 174 | } 175 | count += n 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /rle/lib_test.go: -------------------------------------------------------------------------------- 1 | package rle 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "testing" 7 | ) 8 | 9 | func TestDecompress(t *testing.T) { 10 | b := "0047FF6F0564206A6F6221" 11 | bits, err := hex.DecodeString(b) 12 | if err != nil { 13 | t.Fatalf(err.Error()) 14 | } 15 | bitreader := bytes.NewReader(bits) 16 | z := NewReader(bitreader) 17 | var bitbuffer bytes.Buffer 18 | bitbuffer.ReadFrom(z) 19 | out := bitbuffer.Bytes() 20 | if string(out) != "Good job!" { 21 | t.Fatalf("expected \"Good job!\" but got \"%s\"", out) 22 | } 23 | } 24 | 25 | func TestCompress(t *testing.T) { 26 | var buf bytes.Buffer 27 | z := NewWriter(&buf) 28 | z.Write([]byte("Goood job!")) 29 | b := "0047fe6f0564206a6f6221d882451d" 30 | if hex.EncodeToString(buf.Bytes()) != b { 31 | t.Fatalf("expected %s but got %s", b, hex.EncodeToString(buf.Bytes())) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /scripts/compress.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | compress_directory() { 4 | filename="$1" 5 | if [[ -z "$filename" ]]; then 6 | echo "filename not set. exiting" 7 | exit 1 8 | fi 9 | if [[ -d "$filename/iterations" ]]; then 10 | echo "compressing $filename/iterations" 11 | tar -zcf "$filename/iterations.tar.gz" "$filename/iterations" 12 | if [[ -n "$filename" ]]; then 13 | rm -rf "$filename/iterations" 14 | fi 15 | fi 16 | 17 | } 18 | 19 | unzip_directory() { 20 | filename="$1" 21 | if [[ -z "$filename" ]]; then 22 | echo "filename not set. exiting" 23 | exit 1 24 | fi 25 | if [[ -f "$filename/iterations.tar.gz" ]]; then 26 | tar -xf "$filename/iterations.tar.gz" -C / 27 | fi 28 | } 29 | 30 | main() { 31 | while read filename; do 32 | compress_directory "$filename" 33 | done 34 | } 35 | 36 | main 37 | -------------------------------------------------------------------------------- /scripts/diff_tracks.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go run wip/roundtrip_mine_train.go > rides/mine-train-gold-rush.roundtrip-thru-go.TD6 4 | 5 | echo "=== game track:" 6 | hexdump -C rides/mine-train-gold-rush.td6 | head -n 10 7 | 8 | echo 9 | echo "=== generated track:" 10 | hexdump -C rides/mine-train-gold-rush.roundtrip-thru-go.TD6 | head -n 10 11 | -------------------------------------------------------------------------------- /server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "log" 10 | "math/rand" 11 | "net/http" 12 | "os" 13 | "path" 14 | "regexp" 15 | "strings" 16 | "text/template" 17 | 18 | "github.com/kevinburke/rct" 19 | "github.com/kevinburke/rct/Godeps/_workspace/src/github.com/gorilla/handlers" 20 | "github.com/kevinburke/rct/genetic" 21 | "github.com/kevinburke/rct/geo" 22 | "github.com/kevinburke/rct/rle" 23 | "github.com/kevinburke/rct/td6" 24 | "github.com/kevinburke/rct/tracks" 25 | ) 26 | 27 | const VERSION = "0.2" 28 | 29 | func loadMember(directory string, pth string) (*genetic.Member, error) { 30 | p := path.Join(directory, fmt.Sprintf("%s.json", pth)) 31 | f, err := os.Open(p) 32 | if err != nil { 33 | return nil, err 34 | } 35 | dec := json.NewDecoder(f) 36 | m := new(genetic.Member) 37 | err = dec.Decode(m) 38 | return m, err 39 | } 40 | 41 | func td6Handler(directory string, templateDirectory string) http.HandlerFunc { 42 | return func(w http.ResponseWriter, r *http.Request) { 43 | iterPath := strings.Replace(r.URL.Path, "/td6", "", 1) 44 | m, err := loadMember(directory, iterPath) 45 | if err != nil { 46 | w.WriteHeader(http.StatusBadRequest) 47 | w.Write([]byte(err.Error())) 48 | return 49 | } 50 | query := r.URL.Query() 51 | completeParam := query.Get("complete") 52 | complete := completeParam == "true" 53 | // For whatever reason RCT2 needs the extension to be in uppercase 54 | shortName := fmt.Sprintf("%s.TD6", truncate(17, m.Id)) 55 | w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", shortName)) 56 | w.Header().Set("Content-Type", "text/td6") 57 | ride := td6.CreateMineTrainRide(m.Track, complete) 58 | for i := 0; i < len(ride.TrackData.Elements); i++ { 59 | fmt.Println(tracks.ElementNames[ride.TrackData.Elements[i].Segment.Type]) 60 | } 61 | bits, err := td6.Marshal(ride) 62 | paddedBits := td6.Pad(bits) 63 | rleWriter := rle.NewWriter(w) 64 | 65 | if err != nil { 66 | w.WriteHeader(http.StatusBadRequest) 67 | w.Write([]byte(err.Error())) 68 | return 69 | } 70 | w.WriteHeader(http.StatusOK) 71 | rleWriter.Write(paddedBits) 72 | } 73 | } 74 | 75 | func renderHandler(directory string, templateDirectory string) http.HandlerFunc { 76 | return func(w http.ResponseWriter, r *http.Request) { 77 | iterPath := strings.Replace(r.URL.Path, "/render", "", 1) 78 | m, err := loadMember(directory, iterPath) 79 | if err != nil { 80 | w.WriteHeader(http.StatusBadRequest) 81 | w.Write([]byte(err.Error())) 82 | return 83 | } 84 | left, _ := geo.Render(m.Track) 85 | b := new(bytes.Buffer) 86 | enc := json.NewEncoder(b) 87 | err = enc.Encode(left) 88 | if err == nil { 89 | w.Header().Set("Content-Type", "application/json; charset=utf-8") 90 | w.WriteHeader(http.StatusOK) 91 | io.Copy(w, b) 92 | } else { 93 | w.WriteHeader(http.StatusBadRequest) 94 | w.Write([]byte(err.Error())) 95 | } 96 | } 97 | } 98 | 99 | type tmplData struct { 100 | Member *genetic.Member 101 | Path string 102 | } 103 | 104 | func truncate(l int, s string) string { 105 | var numRunes = 0 106 | for index, _ := range s { 107 | numRunes++ 108 | if numRunes > l { 109 | return s[:index] 110 | } 111 | } 112 | return s 113 | } 114 | 115 | func newRctHandler(directory string, templateDirectory string) http.HandlerFunc { 116 | return func(w http.ResponseWriter, r *http.Request) { 117 | m, err := loadMember(directory, r.URL.Path) 118 | if err != nil { 119 | w.WriteHeader(http.StatusBadRequest) 120 | w.Write([]byte(err.Error())) 121 | return 122 | } 123 | 124 | funcMap := template.FuncMap{ 125 | "truncate": truncate, 126 | } 127 | 128 | tmpl, err := template.New("iteration.html").Funcs(funcMap).ParseFiles(path.Join(templateDirectory, "iteration.html")) 129 | if err != nil { 130 | w.WriteHeader(http.StatusBadRequest) 131 | w.Write([]byte(err.Error())) 132 | return 133 | } 134 | b := new(bytes.Buffer) 135 | err = tmpl.Execute(b, tmplData{Member: m, Path: r.URL.Path}) 136 | if err == nil { 137 | w.WriteHeader(http.StatusOK) 138 | io.Copy(w, b) 139 | } else { 140 | w.WriteHeader(http.StatusBadRequest) 141 | w.Write([]byte(err.Error())) 142 | } 143 | } 144 | } 145 | 146 | type route struct { 147 | pattern *regexp.Regexp 148 | handler http.Handler 149 | } 150 | 151 | type RegexpHandler struct { 152 | routes []*route 153 | } 154 | 155 | func (h *RegexpHandler) Handler(pattern *regexp.Regexp, handler http.Handler) { 156 | h.routes = append(h.routes, &route{pattern, handler}) 157 | } 158 | 159 | func (h *RegexpHandler) HandleFunc(pattern *regexp.Regexp, handler func(http.ResponseWriter, *http.Request)) { 160 | h.routes = append(h.routes, &route{pattern, http.HandlerFunc(handler)}) 161 | } 162 | 163 | func (h *RegexpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 164 | for _, route := range h.routes { 165 | if route.pattern.MatchString(r.URL.Path) { 166 | route.handler.ServeHTTP(w, r) 167 | return 168 | } 169 | } 170 | http.NotFound(w, r) 171 | } 172 | 173 | func serverHeaderHandler(h http.Handler) http.Handler { 174 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 175 | w.Header().Set("Server", fmt.Sprintf("rct/%s", VERSION)) 176 | for { 177 | rint := rand.Intn(len(rct.RIDENAMES)) 178 | if rct.RIDENAMES[rint] == "(none)" { 179 | continue 180 | } 181 | w.Header().Set("X-Powered-By", rct.RIDENAMES[rint]) 182 | break 183 | } 184 | h.ServeHTTP(w, r) 185 | }) 186 | } 187 | 188 | func jsonStripper(h http.Handler) http.Handler { 189 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 190 | if strings.HasSuffix(r.URL.Path, ".json") { 191 | newUrl := strings.TrimSuffix(r.URL.Path, ".json") 192 | http.Redirect(w, r, newUrl, http.StatusMovedPermanently) 193 | return 194 | } 195 | h.ServeHTTP(w, r) 196 | }) 197 | } 198 | 199 | func buildRoute(regex string) *regexp.Regexp { 200 | route, err := regexp.Compile(regex) 201 | if err != nil { 202 | log.Fatal(err) 203 | } 204 | return route 205 | } 206 | 207 | func main() { 208 | directory := flag.String("server-directory", "/usr/local/rct", "Path to the folder storing RCT experiment data") 209 | templateDirectory := flag.String("template-directory", "/usr/local/rct/server/templates", "Path to the folder storing RCT server templates (this file -> server/templates)") 210 | staticDirectory := flag.String("static-directory", "/usr/local/rct/server/static", "Path to the folder storing RCT server templates (this file -> server/static)") 211 | 212 | flag.Parse() 213 | if len(flag.Args()) > 0 { 214 | log.Fatalf("Usage: server [-directory DIRECTORY] ") 215 | } 216 | h := new(RegexpHandler) 217 | td6Route := buildRoute("/experiments/.+/iterations/.+/.+/td6") 218 | renderRoute := buildRoute("/experiments/.*/iterations/.+/.+/render") 219 | iterationRoute := buildRoute("/experiments/.+/iterations/.+/.+") 220 | staticRoute := buildRoute("/static") 221 | defaultRoute := buildRoute("/") 222 | 223 | h.HandleFunc(td6Route, td6Handler(*directory, *templateDirectory)) 224 | h.HandleFunc(renderRoute, renderHandler(*directory, *templateDirectory)) 225 | h.HandleFunc(iterationRoute, newRctHandler(*directory, *templateDirectory)) 226 | h.Handler(staticRoute, http.StripPrefix("/static", http.FileServer(http.Dir(*staticDirectory)))) 227 | h.Handler(defaultRoute, http.FileServer(http.Dir(*directory))) 228 | allHandlers := jsonStripper(serverHeaderHandler(h)) 229 | log.Fatal(http.ListenAndServe(":8080", handlers.LoggingHandler(os.Stdout, allHandlers))) 230 | } 231 | -------------------------------------------------------------------------------- /server/static/readme.md: -------------------------------------------------------------------------------- 1 | offline jquery: 2 | 3 | You can achieve it like this: 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | This should be in your page's and any jQuery ready event handlers should be in the to avoid errors (although it's not fool-proof!). 13 | 14 | One more reason to not use Google-hosted jQuery is that in some countries, Google's domain name is banned. 15 | -------------------------------------------------------------------------------- /server/templates/iteration.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RCT2 Iteration Viewer 5 | 6 | 7 | 8 | 14 | 15 | 16 |

{{ .Member.Id | truncate 17 }}

17 | 18 |

19 | Score: {{ .Member.Score }}
20 | Fitness: {{ .Member.Fitness }} 21 |

22 | 23 |
24 | download coaster file
25 | download completed coaster file 26 |
27 | 28 |
29 | 30 | 31 | 32 | 33 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /td4/td4.go: -------------------------------------------------------------------------------- 1 | // td4 ride format parser 2 | package td4 3 | 4 | import ( 5 | "bytes" 6 | "encoding/hex" 7 | "errors" 8 | "fmt" 9 | "io/ioutil" 10 | 11 | "github.com/kevinburke/rct/bits" 12 | "github.com/kevinburke/rct/rle" 13 | "github.com/kevinburke/rct/tracks" 14 | ) 15 | 16 | func hasBit(n int, pos uint) bool { 17 | return bits.On(n, pos) 18 | } 19 | 20 | func hasLoop(n int) bool { 21 | return hasBit(n, 7) 22 | } 23 | 24 | // Set the loop bit. 25 | func marshalLoop(hasLoop bool, n int) { 26 | if hasLoop { 27 | n |= (1 << 7) 28 | } else { 29 | n &= ^(1 << 7) 30 | } 31 | } 32 | 33 | type ControlFlags struct { 34 | UseMaximumTime bool 35 | UseMinimumTime bool 36 | SyncWithAdjacentStation bool 37 | LeaveIfAnotherTrainArrives bool 38 | WaitForLoad bool 39 | Load LoadType 40 | } 41 | 42 | func parseControlFlags(n int) *ControlFlags { 43 | return &ControlFlags{ 44 | UseMaximumTime: n>>7 == 1, 45 | UseMinimumTime: n>>6 == 1, 46 | SyncWithAdjacentStation: n>>5 == 1, 47 | LeaveIfAnotherTrainArrives: n>>4 == 1, 48 | WaitForLoad: n>>3 == 1, 49 | Load: LoadType(n & 7), 50 | } 51 | } 52 | 53 | type RideType int 54 | type VehicleType int 55 | type LoadType int 56 | type OperatingMode int 57 | 58 | const ( 59 | WOODEN_RIDE RideType = iota 60 | STAND_UP_STEEL = iota 61 | SUSPENDED = iota 62 | ) 63 | 64 | const ( 65 | STEEL VehicleType = iota 66 | STEEL_BACKWARDS = iota 67 | WOODEN_VEHICLE = iota 68 | INVERTED = iota 69 | SUSPENDED_SWINGING = iota 70 | LADYBIRD = iota 71 | STAND_UP = iota 72 | ) 73 | 74 | const ( 75 | MODE_NORMAL OperatingMode = 0x00 76 | MODE_CONTINUOUS_CIRCUIT = 0x01 77 | MODE_REVERSE_INCLINE_SHUTTLE = 0x02 78 | MODE_POWERED_LAUNCH = 0x03 79 | MODE_SHUTTLE = 0x04 80 | MODE_BOAT_HIRE = 0x05 81 | MODE_UPWARD_LAUNCH = 0x06 82 | MODE_ROTATING_LIFT = 0x07 83 | MODE_STATION_TO_STATION = 0x08 84 | ) 85 | 86 | const ( 87 | LOAD_ONE_QUARTER = 0 88 | LOAD_HALF = 1 89 | LOAD_THREE_QUARTERS = 2 90 | LOAD_FULL = 3 91 | LOAD_ANY = 4 92 | ) 93 | 94 | const ( 95 | IDX_RIDE_TYPE = 0 96 | IDX_VEHICLE_TYPE = 1 97 | IDX_HAS_LOOP = 2 98 | IDX_OPERATING_MODE = 0x06 99 | IDX_CONTROL_FLAG = 0x23 100 | IDX_NUM_TRAINS = 0x24 101 | IDX_CARS_PER_TRAIN = 0x25 102 | IDX_MIN_WAIT_TIME = 0x26 103 | IDX_MAX_WAIT_TIME = 0x27 104 | IDX_TRACK_DATA = 0xc4 105 | ) 106 | 107 | type Ride struct { 108 | RideType RideType 109 | VehicleType VehicleType 110 | HasLoop bool 111 | OperatingMode OperatingMode 112 | ControlFlags *ControlFlags 113 | NumTrains int 114 | CarsPerTrain int 115 | MinWaitTime int 116 | MaxWaitTime int 117 | TrackData tracks.Data 118 | } 119 | 120 | type StationType int 121 | 122 | const ( 123 | STATION_ONE StationType = 0 124 | STATION_TWO = 1 125 | STATION_THREE = 2 126 | STATION_FOUR = 3 127 | ) 128 | 129 | // Take a compressed byte stream representing a ride and turn it into a Ride 130 | // struct. Returns an error if the byte array is too short. 131 | func Unmarshal(buf []byte, r *Ride) error { 132 | if len(buf) < IDX_TRACK_DATA { 133 | return errors.New("buffer too short to be a ride") 134 | } 135 | r.RideType = RideType(buf[IDX_RIDE_TYPE]) 136 | r.VehicleType = VehicleType(buf[IDX_VEHICLE_TYPE]) 137 | r.HasLoop = hasLoop(int(buf[IDX_HAS_LOOP])) 138 | r.OperatingMode = OperatingMode(buf[IDX_OPERATING_MODE]) 139 | r.ControlFlags = parseControlFlags(int(buf[IDX_CONTROL_FLAG])) 140 | r.NumTrains = int(buf[IDX_NUM_TRAINS]) 141 | r.CarsPerTrain = int(buf[IDX_CARS_PER_TRAIN]) 142 | r.MinWaitTime = int(buf[IDX_MIN_WAIT_TIME]) 143 | r.MaxWaitTime = int(buf[IDX_MAX_WAIT_TIME]) 144 | r.TrackData = parseTrackData(buf[IDX_TRACK_DATA:]) 145 | 146 | fmt.Println(r.VehicleType) 147 | return nil 148 | } 149 | 150 | func Marshal(r *Ride) ([]byte, error) { 151 | // at a minimum, rides have this much data 152 | bits := make([]byte, 0xc4) 153 | 154 | // This is a little bit tricky, and requires implementing the format 155 | // described in the tycoon technical depot, available here: 156 | // https://github.com/UnknownShadow200/RCTTechDepot-Archive/blob/master/td4.html 157 | bits[IDX_RIDE_TYPE] = byte(r.RideType) 158 | bits[IDX_VEHICLE_TYPE] = byte(r.VehicleType) 159 | 160 | return bits, nil 161 | } 162 | 163 | var EndOfRide = errors.New("End of ride") 164 | 165 | // Parse the serialized data into a TrackElement struct 166 | // When the end of ride is encountered a EndOfRide error is returned 167 | func parseElement(rawElement []byte) (te tracks.Element, e error) { 168 | if len(rawElement) != 2 { 169 | return tracks.Element{}, errors.New("invalid length for element input") 170 | } 171 | te.Segment = &tracks.Segment{ 172 | Type: tracks.SegmentType(rawElement[0]), 173 | } 174 | if te.Segment.Type == tracks.ELEM_END_OF_RIDE { 175 | return tracks.Element{}, EndOfRide 176 | } 177 | q := int(rawElement[1]) 178 | te.ChainLift = q>>7 == 1 179 | te.InvertedTrack = q>>6 == 1 180 | te.Station = q>>3 == 1 181 | te.StationNumber = q & 3 182 | 183 | te.BoostMagnitude = float32(q&15) * 7.6 184 | te.Rotation = (q&15)*45 - 180 185 | return 186 | } 187 | 188 | func parseTrackData(trackData []byte) tracks.Data { 189 | td := new(tracks.Data) 190 | for i := 0; i < len(trackData); i += 2 { 191 | elem, err := parseElement(trackData[i : i+2]) 192 | if err != nil { 193 | if err == EndOfRide { 194 | break 195 | } 196 | panic(err) 197 | } 198 | td.Elements = append(td.Elements, elem) 199 | } 200 | 201 | // XXX where is this data actually stored? 202 | td.Clearance = 2 203 | td.ClearanceDirection = tracks.CLEARANCE_ABOVE 204 | return *td 205 | } 206 | 207 | func main() { 208 | fmt.Println(readRaptor()) 209 | } 210 | 211 | func readRaptor() Ride { 212 | encodedBits, err := ioutil.ReadFile("rides/raptor.td4") 213 | if err != nil { 214 | panic(err) 215 | } 216 | z := rle.NewReader(bytes.NewReader(encodedBits)) 217 | if err != nil { 218 | panic(err) 219 | } 220 | var bitbuffer bytes.Buffer 221 | bitbuffer.ReadFrom(z) 222 | decrypted := bitbuffer.Bytes() 223 | for i := 0; i < 40; i++ { 224 | // encode the value of i as hex 225 | ds := hex.EncodeToString([]byte{byte(i)}) 226 | bitValueInHex := hex.EncodeToString([]byte{decrypted[i]}) 227 | fmt.Printf("%s: %s\n", ds, bitValueInHex) 228 | } 229 | 230 | // r is a pointer 231 | r := new(Ride) 232 | Unmarshal(decrypted, r) 233 | bits, err := Marshal(r) 234 | fmt.Println(bits) 235 | return *r 236 | } 237 | -------------------------------------------------------------------------------- /td6/egress.go: -------------------------------------------------------------------------------- 1 | // Dealing with entrances/exits from a ride 2 | package td6 3 | 4 | import ( 5 | "bytes" 6 | "encoding/binary" 7 | 8 | "github.com/kevinburke/rct/bits" 9 | ) 10 | 11 | type Egress struct { 12 | Exit bool 13 | Direction int 14 | XOffset int16 15 | YOffset int16 16 | } 17 | 18 | // Get list of entrances/exits for a ride from raw data 19 | // Copied mostly from "Scenery Items" here: 20 | // http://freerct.github.io/RCTTechDepot-Archive/TD6.html 21 | func unmarshalEgress(buf []byte) []*Egress { 22 | var egrs []*Egress 23 | for i := 0; true; i += 6 { 24 | if buf[i] == 0xff { 25 | break 26 | } 27 | features := int(buf[i+1]) 28 | egr := &Egress{ 29 | Exit: bits.On(features, 7), 30 | // Direction set in lower two bits 31 | Direction: features & 3, 32 | XOffset: int16(binary.LittleEndian.Uint16(buf[i+2 : i+4])), 33 | YOffset: int16(binary.LittleEndian.Uint16(buf[i+4 : i+6])), 34 | } 35 | egrs = append(egrs, egr) 36 | } 37 | return egrs 38 | } 39 | 40 | func marshalEgress(egr Egress) ([]byte, error) { 41 | buf := make([]byte, 6) 42 | ftrBit := egr.Direction 43 | ftrBit = bits.SetCond(ftrBit, 7, egr.Exit) 44 | buf[1] = byte(ftrBit) 45 | 46 | b := new(bytes.Buffer) 47 | err := binary.Write(b, binary.LittleEndian, egr.XOffset) 48 | if err != nil { 49 | return []byte{}, err 50 | } 51 | copy(buf[2:4], b.Bytes()) 52 | b.Reset() 53 | err = binary.Write(b, binary.LittleEndian, egr.YOffset) 54 | if err != nil { 55 | return []byte{}, err 56 | } 57 | copy(buf[4:6], b.Bytes()) 58 | return buf, nil 59 | } 60 | 61 | func marshalEgresses(egrs []*Egress) ([]byte, error) { 62 | buf := make([]byte, 6*len(egrs)+1) 63 | for i, egr := range egrs { 64 | bts, err := marshalEgress(*egr) 65 | if err != nil { 66 | return []byte{}, err 67 | } 68 | copy(buf[i*6:i*6+6], bts) 69 | } 70 | buf[len(buf)-1] = 0xff 71 | return buf, nil 72 | } 73 | -------------------------------------------------------------------------------- /td6/lib_test.go: -------------------------------------------------------------------------------- 1 | package td6 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/kevinburke/rct/bits" 7 | ) 8 | 9 | func testMarshalControlFlags(t *testing.T) { 10 | t.Parallel() 11 | cFlags := ControlFlags{ 12 | Load: 3, 13 | UseMaximumTime: true, 14 | } 15 | n := marshalControlFlags(cFlags) 16 | if !bits.On(n, 7) { 17 | t.Errorf("Maximum time bit should have been set, but wasn't, n is %d", n) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /td6/spatial.go: -------------------------------------------------------------------------------- 1 | package td6 2 | -------------------------------------------------------------------------------- /td6_runners/excitement.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "fmt" 7 | "io/ioutil" 8 | 9 | "github.com/kevinburke/rct/rle" 10 | "github.com/kevinburke/rct/td6" 11 | ) 12 | 13 | func main() { 14 | r := td6.ReadRide("rides/mischief.td6") 15 | 16 | fmt.Println(r.Excitement) 17 | fmt.Println(r.Intensity) 18 | fmt.Println(r.Nausea) 19 | fmt.Println(r.MaxSpeed) 20 | fmt.Println(r.AverageSpeed) 21 | 22 | fmt.Printf("Track length: %d\n", len(r.TrackData.Elements)) 23 | 24 | bits, err := td6.Marshal(r) 25 | if err != nil { 26 | panic(err) 27 | } 28 | 29 | //for i := range bits { 30 | //if bits[i] != decrypted[i] { 31 | //fmt.Printf("%d: ", i) 32 | //fmt.Printf("byte %x differs, in my mischief it is %d, in orig it is %d\n", i, bits[i], decrypted[i]) 33 | //} 34 | //} 35 | 36 | if td6.DEBUG { 37 | begin := 0xa2 + 2*len(r.TrackData.Elements) - 3 38 | for i := begin; i < begin+td6.DEBUG_LENGTH; i++ { 39 | // encode the value of i as hex 40 | ds := hex.EncodeToString([]byte{byte(i)}) 41 | bitValueInHex := hex.EncodeToString([]byte{bits[i]}) 42 | fmt.Printf("%s: %s\n", ds, bitValueInHex) 43 | } 44 | } 45 | 46 | paddedBits := td6.Pad(bits) 47 | 48 | //fmt.Println(paddedBits) 49 | //fmt.Println(decrypted) 50 | 51 | var buf bytes.Buffer 52 | w := rle.NewWriter(&buf) 53 | w.Write(paddedBits) 54 | ioutil.WriteFile("/Users/kevin/Applications/Wineskin/rct2.app/Contents/Resources/drive_c/GOG Games/RollerCoaster Tycoon 2 Triple Thrill Pack/Tracks/mymischief.TD6", buf.Bytes(), 0644) 55 | fmt.Println("Wrote rides/mischief.td6.out.") 56 | } 57 | -------------------------------------------------------------------------------- /tracks/branch_factor/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/kevinburke/rct/tracks" 7 | ) 8 | 9 | func main() { 10 | for i := range tracks.ElementNames { 11 | seg := tracks.TS_MAP[tracks.SegmentType(i)] 12 | elem := tracks.Element{ 13 | Segment: seg, 14 | } 15 | if !tracks.Valid(seg) { 16 | continue 17 | } 18 | fmt.Println(len(elem.Possibilities())) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tracks/segmentfns_test.go: -------------------------------------------------------------------------------- 1 | package tracks 2 | 3 | import "testing" 4 | 5 | func elem(s SegmentType) Element { 6 | return Element{ 7 | Segment: TS_MAP[s], 8 | } 9 | } 10 | 11 | func TestNotPossible(t *testing.T) { 12 | testCases := []struct { 13 | input Element 14 | notPossibility Element 15 | }{ 16 | {elem(ELEM_FLAT_TO_25_DEG_UP), elem(ELEM_LEFT_QUARTER_TURN_5_TILES)}, 17 | } 18 | for _, tt := range testCases { 19 | possibilities := tt.input.Possibilities() 20 | for _, poss := range possibilities { 21 | if poss.Segment.Type == tt.notPossibility.Segment.Type { 22 | t.Errorf("expected %s to not be a possibility for %s, but was", poss.Segment.String(), tt.notPossibility.Segment.String()) 23 | } 24 | } 25 | } 26 | } 27 | 28 | func TestCompatible(t *testing.T) { 29 | testCases := []struct { 30 | first Element 31 | second Element 32 | compatible bool 33 | }{ 34 | {elem(ELEM_FLAT_TO_25_DEG_UP), elem(ELEM_LEFT_QUARTER_TURN_5_TILES), false}, 35 | {elem(ELEM_LEFT_QUARTER_TURN_3_TILES_25_DEG_DOWN), elem(ELEM_LEFT_BANK), false}, 36 | } 37 | for _, tt := range testCases { 38 | compat := Compatible(tt.first, tt.second) 39 | if compat != tt.compatible { 40 | t.Errorf("Compatible: first: %s, next: %s, want %t, got %t", tt.first.Segment.String(), tt.second.Segment.String(), tt.compatible, compat) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tracks/track.go: -------------------------------------------------------------------------------- 1 | // A whole bunch of data relating to tracks. 2 | 3 | // This menu table page is information about the RCT1 saved game files. Those 4 | // addresses are for .SV4 files. In RCT2, the ride types are in a different 5 | // order/arrangement. Some rides and shops use the same type as this is how 6 | // custom objects work. 7 | 8 | package tracks 9 | 10 | import ( 11 | "encoding/hex" 12 | "errors" 13 | "fmt" 14 | 15 | "github.com/kevinburke/rct/bits" 16 | ) 17 | 18 | // An Element containts a single track segment and some metadata about the 19 | // track segment - whether it is a station piece, or has a chain lift. 20 | type Element struct { 21 | // XXX, add color schemes 22 | 23 | Segment *Segment 24 | ChainLift bool 25 | InvertedTrack bool 26 | Station bool 27 | StationNumber int 28 | 29 | // bits 3, 2, 1 and 0 are a magnitude value (0..15) for brake and booster 30 | // track segments. The value obtained from these four bits is multiplied 31 | // by 7.6 km/hr = 4.5 mph. 32 | // 33 | // We store the value in km/h 34 | BoostMagnitude float32 35 | 36 | // For RCT2 "Multi Dimensional Coaster", these four bits specify the amount 37 | // of rotation. 38 | Rotation int 39 | } 40 | 41 | type Data struct { 42 | Elements []Element 43 | Clearance int 44 | ClearanceDirection ClearanceDirection 45 | } 46 | 47 | // The amount of space each track piece needs above or below. XXX should this 48 | // go on the ride object? 49 | type ClearanceDirection int 50 | 51 | const ( 52 | CLEARANCE_ABOVE = iota 53 | // For suspended coasters 54 | CLEARANCE_BELOW = iota 55 | ) 56 | 57 | func (te Element) String() string { 58 | return fmt.Sprintf("%s: %s", hex.EncodeToString([]byte{byte(te.Segment.Type)}), 59 | string(ElementNames[te.Segment.Type])) 60 | } 61 | 62 | var EndOfRide = errors.New("End of ride") 63 | 64 | // Parse the serialized data into a Element struct 65 | // When the end of ride is encountered a EndOfRide error is returned 66 | // Documentation from 67 | // http://freerct.github.io/RCTTechDepot-Archive/trackQualifier.html 68 | func unmarshalElement(rawElement []byte) (te Element, e error) { 69 | if len(rawElement) != 2 { 70 | return Element{}, errors.New("invalid length for element input") 71 | } 72 | // XXX hack for brakes 73 | if rawElement[0] == 0xd8 { 74 | rawElement[0] = ELEM_BRAKES 75 | } 76 | te.Segment = TS_MAP[SegmentType(rawElement[0])] 77 | if te.Segment.Type == ELEM_END_OF_RIDE { 78 | return Element{}, EndOfRide 79 | } 80 | q := int(rawElement[1]) 81 | te.ChainLift = bits.On(q, 7) 82 | te.InvertedTrack = bits.On(q, 6) 83 | te.Station = bits.On(q, 3) 84 | if te.Station { 85 | fmt.Println("found station piece") 86 | fmt.Println(te.Segment) 87 | } 88 | te.StationNumber = q & 3 89 | 90 | te.BoostMagnitude = float32(q&15) * 7.6 91 | te.Rotation = (q&15)*45 - 180 92 | return 93 | } 94 | 95 | // Turn track elements back into bytes (opposite of above function) 96 | // 97 | // Taken from http://freerct.github.io/RCTTechDepot-Archive/trackQualifier.html 98 | func marshalElement(e Element) ([]byte, error) { 99 | buf := make([]byte, 2) 100 | buf[0] = byte(e.Segment.Type) 101 | featureBit := 0 102 | featureBit = bits.SetCond(featureBit, 7, e.ChainLift) 103 | featureBit = bits.SetCond(featureBit, 6, e.InvertedTrack) 104 | featureBit = bits.SetCond(featureBit, 3, e.Station) 105 | if e.Segment.Type == ELEM_END_STATION || e.Segment.Type == ELEM_BEGIN_STATION || e.Segment.Type == ELEM_MIDDLE_STATION { 106 | // Set the station bit 107 | featureBit |= e.StationNumber 108 | } 109 | 110 | // XXX booster? 111 | if e.Segment.Type == ELEM_BRAKES || e.Segment.Type == ELEM_BLOCK_BRAKES { 112 | bm := e.BoostMagnitude / 7.6 113 | featureBit |= int(bm) 114 | } else { 115 | // 2nd bit seems to be set in most cases. 116 | featureBit |= 1 << 2 117 | } 118 | // XXX, rotation for multi dimensional coasters 119 | buf[1] = byte(featureBit) 120 | return buf, nil 121 | } 122 | 123 | // Turn RCT encoded track data into a Data object. 124 | func Unmarshal(buf []byte, d *Data) error { 125 | for i := 0; i < len(buf); i += 2 { 126 | elem, err := unmarshalElement(buf[i : i+2]) 127 | if err != nil { 128 | if err == EndOfRide { 129 | break 130 | } 131 | return err 132 | } 133 | d.Elements = append(d.Elements, elem) 134 | } 135 | 136 | // XXX where is this data actually stored? 137 | d.Clearance = 2 138 | d.ClearanceDirection = CLEARANCE_ABOVE 139 | return nil 140 | } 141 | 142 | // Turn track data into a series of bytes 143 | func Marshal(d *Data) ([]byte, error) { 144 | buf := make([]byte, 2*len(d.Elements)+1) 145 | for i := range d.Elements { 146 | bts, err := marshalElement(d.Elements[i]) 147 | if err != nil { 148 | return []byte{}, err 149 | } 150 | copy(buf[i*2:i*2+2], bts) 151 | } 152 | buf[len(buf)-1] = 0xff 153 | return buf, nil 154 | } 155 | -------------------------------------------------------------------------------- /tracks/track_test.go: -------------------------------------------------------------------------------- 1 | package tracks 2 | 3 | import "testing" 4 | 5 | func TestPossibilities(t *testing.T) { 6 | steep := TS_MAP[ELEM_FLAT] 7 | elem := Element{Segment: steep} 8 | poss := elem.Possibilities() 9 | for _, p := range poss { 10 | if p.Segment.Type == 0xff { 11 | t.Errorf("should not have contained the end of ride but did") 12 | } 13 | if p.Segment.Type == ELEM_END_STATION { 14 | t.Errorf("should not be possible to build a station") 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /wip/compare.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | ) 7 | 8 | func main() { 9 | f, err := ioutil.ReadFile("/Users/kevin/Applications/Wineskin/rct2.app/Contents/Resources/drive_c/GOG Games/RollerCoaster Tycoon 2 Triple Thrill Pack/Tracks/mymischief.td6") 10 | g, err := ioutil.ReadFile("/Users/kevin/code/go/src/github.com/kevinburke/rct/rides/mischief.td6") 11 | 12 | if err != nil { 13 | fmt.Errorf(err.Error()) 14 | } 15 | 16 | for i := range f { 17 | if f[i] != g[i] { 18 | fmt.Printf("byte %x differs, in my mischief it is %d, in orig it is %d\n", i, f[i], g[i]) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /wip/funparser.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "go/parser" 7 | "go/token" 8 | ) 9 | 10 | func main() { 11 | fset := token.NewFileSet() 12 | f, err := parser.ParseFile(fset, "tracks/segment.go", nil, parser.AllErrors) 13 | if err != nil { 14 | panic(err) 15 | } 16 | fmt.Println(f.Decls) 17 | for _, elem := range f.Decls { 18 | switch x := elem.(type) { 19 | case *ast.GenDecl: 20 | if x.Tok == token.VAR { 21 | fmt.Println("it is a var") 22 | fmt.Println(x.Tok) 23 | spec := x.Specs[0].(*ast.ValueSpec) 24 | if spec.Names[0].String() == "TS_MAP" { 25 | fmt.Println("found the ts map") 26 | } 27 | vls := spec.Values 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /wip/inspect.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type CustomType int 9 | 10 | const ( 11 | FOO_BAR CustomType = 3 12 | ) 13 | 14 | func main() { 15 | a := FOO_BAR 16 | v := reflect.ValueOf(a) 17 | fmt.Println(v.Elem()) 18 | } 19 | -------------------------------------------------------------------------------- /wip/inspect_mine_train.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/kevinburke/rct/td6" 7 | ) 8 | 9 | func main() { 10 | ride := td6.ReadRide("rides/mine-train-gold-rush.td6") 11 | fmt.Printf("%#v\n", ride) 12 | } 13 | -------------------------------------------------------------------------------- /wip/printvalue.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Blah struct { 6 | Bar int 7 | Baz string 8 | Baz1 string 9 | Baz2 string 10 | Baz3 string 11 | Baz4 string 12 | Baz5 string 13 | Baz6 string 14 | Baz7 string 15 | Baz8 string 16 | Baz9 string 17 | Baz10 string 18 | Baz11 string 19 | Baz12 string 20 | Baz13 string 21 | Baz14 string 22 | Baz15 string 23 | Baz16 string 24 | Baz17 string 25 | Baz18 string 26 | Baz19 string 27 | Baz20 string 28 | Baz21 string 29 | } 30 | 31 | func main() { 32 | bozz := &Blah{ 33 | Bar: 8, 34 | Baz: "seven", 35 | } 36 | fmt.Printf("%#v\n", bozz) 37 | } 38 | -------------------------------------------------------------------------------- /wip/read_has_running_track.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | rct "github.com/kevinburke/rct" 8 | ) 9 | 10 | func main() { 11 | 12 | f, err := os.Open(os.Getenv("HOME") + "/code/OpenRCT2/openrct2.exe") 13 | if err != nil { 14 | panic(err) 15 | } 16 | defer f.Close() 17 | 18 | var WIDTH = 0x12 19 | b := make([]byte, 100*WIDTH) 20 | addr := 0x0057E3AC 21 | f.ReadAt(b, int64(addr)) // direction change stored in 2nd bit. 22 | 23 | for i := 0; i < len(rct.RIDENAMES); i++ { 24 | //fmt.Printf("%2x ", i) 25 | //fmt.Printf("%40s ", rct.RIDENAMES[i]) 26 | var val bool 27 | if b[i*WIDTH] == 20 { 28 | val = true 29 | } else { 30 | val = false 31 | } 32 | fmt.Printf("\t%t,\t", bool(val)) 33 | fmt.Printf("// %x %s\n", i, rct.RIDENAMES[i]) 34 | 35 | //fmt.Printf("%t\n", b[i*WIDTH]&0x80 > 0) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /wip/read_ride_97D4F2_table.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/kevinburke/rct" 8 | ) 9 | 10 | func main() { 11 | f, err := os.Open(os.Getenv("HOME") + "/code/OpenRCT2/openrct2.exe") 12 | if err != nil { 13 | panic(err) 14 | } 15 | defer f.Close() 16 | 17 | var WIDTH = 8 18 | b := make([]byte, 100*WIDTH) 19 | // all 4 bytes here have significance on their own, somehow 20 | addr := 0x0057D4F2 21 | f.ReadAt(b, int64(addr)) // direction change stored in 2nd bit. 22 | 23 | for i := 0; i < len(rct.RIDENAMES); i++ { 24 | fmt.Printf("%2x ", i) 25 | fmt.Printf("%40s ", rct.RIDENAMES[i]) 26 | fmt.Printf("%5d ", b[i*WIDTH]) 27 | fmt.Printf("%t\n", b[i*WIDTH]&0x80 > 0) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /wip/read_ride_97D7C9_table.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | rct "github.com/kevinburke/rct" 8 | ) 9 | 10 | func main() { 11 | f, err := os.Open(os.Getenv("HOME") + "/code/OpenRCT2/openrct2.exe") 12 | if err != nil { 13 | panic(err) 14 | } 15 | defer f.Close() 16 | 17 | var WIDTH = 4 18 | b := make([]byte, 100*WIDTH) 19 | // all 4 bytes here have significance on their own, somehow 20 | addr := 0x0057D7C9 21 | f.ReadAt(b, int64(addr)) // direction change stored in 2nd bit. 22 | 23 | for i := 0; i < len(rct.RIDENAMES); i++ { 24 | fmt.Printf("%2x ", i) 25 | fmt.Printf("%40s ", rct.RIDENAMES[i]) 26 | fmt.Printf("%d\n", b[i*WIDTH]) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /wip/read_ride_97E3A8_table.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | rct "github.com/kevinburke/rct" 8 | ) 9 | 10 | func main() { 11 | f, err := os.Open(os.Getenv("HOME") + "/code/OpenRCT2/openrct2.exe") 12 | if err != nil { 13 | panic(err) 14 | } 15 | defer f.Close() 16 | 17 | var WIDTH = 0x12 18 | b := make([]byte, 100*WIDTH) 19 | // all 4 bytes here have significance on their own, somehow 20 | addr := 0x0057E3A8 21 | f.ReadAt(b, int64(addr)) // direction change stored in 2nd bit. 22 | 23 | for i := 0; i < len(rct.RIDENAMES); i++ { 24 | fmt.Printf("\t%d,\t", b[i*WIDTH]) 25 | fmt.Printf("// %02x ", i) 26 | fmt.Printf("%s\n", rct.RIDENAMES[i]) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /wip/read_ride_97E3AA_table.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | rct "github.com/kevinburke/rct" 8 | ) 9 | 10 | func main() { 11 | f, err := os.Open(os.Getenv("HOME") + "/code/OpenRCT2/openrct2.exe") 12 | if err != nil { 13 | panic(err) 14 | } 15 | defer f.Close() 16 | 17 | var WIDTH = 0x12 18 | b := make([]byte, 100*WIDTH) 19 | addr := 0x0057E3B8 20 | f.ReadAt(b, int64(addr)) 21 | 22 | for i := 0; i < len(rct.RIDENAMES); i++ { 23 | fmt.Printf("\t%d,\t", b[i*WIDTH]) 24 | fmt.Printf("// %02x ", i) 25 | fmt.Printf("%s\n", rct.RIDENAMES[i]) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /wip/read_ride_97E3B6_table.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | rct "github.com/kevinburke/rct" 8 | ) 9 | 10 | func main() { 11 | f, err := os.Open(os.Getenv("HOME") + "/code/OpenRCT2/openrct2.exe") 12 | if err != nil { 13 | panic(err) 14 | } 15 | defer f.Close() 16 | 17 | var WIDTH = 0x12 18 | b := make([]byte, 100*WIDTH) 19 | addr := 0x0057E3B6 20 | f.ReadAt(b, int64(addr)) 21 | 22 | for i := 0; i < len(rct.RIDENAMES); i++ { 23 | if b[i*WIDTH] == 3 { 24 | fmt.Printf("\ttrue,\t") 25 | } else { 26 | fmt.Printf("\tfalse,\t") 27 | } 28 | fmt.Printf("// %02x ", i) 29 | fmt.Printf("%s\n", rct.RIDENAMES[i]) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /wip/read_ride_table.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func hasBit(n int, pos uint) bool { 9 | val := n & (1 << pos) 10 | return (val > 0) 11 | } 12 | 13 | func main() { 14 | f, err := os.Open(os.Getenv("HOME") + "/code/OpenRCT2/openrct2.exe") 15 | if err != nil { 16 | panic(err) 17 | } 18 | defer f.Close() 19 | b := make([]byte, 300) 20 | f.ReadAt(b, 0x0019889C) 21 | fmt.Println(b) 22 | for i := 0; i < 20; i++ { 23 | fmt.Printf("Ride %d\n", i) 24 | onebyte := int(b[i*4]) 25 | if hasBit(onebyte, 1) { 26 | fmt.Println("has flat track") 27 | } 28 | if hasBit(onebyte, 2) { 29 | fmt.Println("has station platform") 30 | } 31 | if hasBit(onebyte, 6) { 32 | fmt.Println("has banking") 33 | } 34 | if hasBit(onebyte, 7) { 35 | fmt.Println("has vertical loop") 36 | } 37 | onebyte = int(b[i*4+1]) 38 | if hasBit(onebyte, 0) { 39 | fmt.Println("has normal slope") 40 | } 41 | if hasBit(onebyte, 1) { 42 | fmt.Println("has steep slope") 43 | } 44 | if hasBit(onebyte, 2) { 45 | fmt.Println("has flat to steep") 46 | } 47 | fmt.Println("\n") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /wip/ride_ratings/compute_var_198.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | rct "github.com/kevinburke/rct" 8 | ) 9 | 10 | func main() { 11 | f, err := os.Open(os.Getenv("HOME") + "/code/OpenRCT2/openrct2.exe") 12 | if err != nil { 13 | panic(err) 14 | } 15 | defer f.Close() 16 | 17 | var WIDTH = 4 18 | b := make([]byte, 100*WIDTH) 19 | // all 4 bytes here have significance on their own, somehow 20 | addr := 0x0097D7C9 21 | f.ReadAt(b, int64(addr)) // direction change stored in 2nd bit. 22 | 23 | for i := 0; i < len(rct.RIDENAMES); i++ { 24 | fmt.Printf("%2x ", i) 25 | fmt.Printf("%40s ", rct.RIDENAMES[i]) 26 | fmt.Printf("%5d ", b[i*WIDTH]) 27 | fmt.Printf("%t\n", b[i*WIDTH]&0x80 > 0) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /wip/ride_ratings/rct-rides: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinburke/rct/5650bb05df22dc5f3da9b4498eff63b57ce9136e/wip/ride_ratings/rct-rides -------------------------------------------------------------------------------- /wip/roundtrip_mine_train.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/kevinburke/rct/rle" 8 | "github.com/kevinburke/rct/td6" 9 | ) 10 | 11 | func main() { 12 | ride := td6.ReadRide("rides/mine-train-gold-rush.td6") 13 | bits, err := td6.Marshal(ride) 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | writer := rle.NewWriter(os.Stdout) 18 | writer.Write(bits) 19 | } 20 | -------------------------------------------------------------------------------- /wip/tmp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "io/ioutil" 6 | ) 7 | 8 | func main() { 9 | b := "0047FF6F0564206A6F6221" 10 | bits, err := hex.DecodeString(b) 11 | if err != nil { 12 | panic(err) 13 | } 14 | ioutil.WriteFile("rides/hello.td4", bits, 0644) 15 | } 16 | --------------------------------------------------------------------------------