├── LICENSE.md
├── README.md
├── ctrl
├── doc.go
└── engine.go
├── golab-screenshot.png
├── golab.go
├── model
├── const.go
├── doc.go
├── embedded_imgs.go
├── images.go
└── lab.go
├── resources
├── bulldog-down.png
├── bulldog-left.png
├── bulldog-right.png
├── bulldog-up.png
├── door.png
├── gopher-dead.png
├── gopher-down.png
├── gopher-left.png
├── gopher-right.png
├── gopher-up.png
├── marker.png
├── source.txt
├── wall.png
└── won.png
└── view
├── const.go
├── doc.go
├── handlers.go
├── help_html.go
└── play_html.go
/LICENSE.md:
--------------------------------------------------------------------------------
1 | GoLab LICENSE
2 | ===
3 |
4 | Copyright (c) 2015 Andras Belicza. All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | - Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | - Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | GoLab
2 | ===
3 |
4 | _This game is reincarnated in [icza/golab](https://github.com/icza/golab)._
5 |
6 |
7 | Introduction
8 | ---
9 |
10 | **Gopher's Labyrinth** (or just **GoLab**) is a 2-dimensional Labyrinth game where you control [Gopher](http://golang.org/doc/gopher/frontpage.png) (who else) and your goal is to get to the Exit point of the Labyrinth. But beware of the bloodthirsty _Bulldogs_, the ancient enemies of gophers who are endlessly roaming the Labyrinth!
11 |
12 | Controlling Gopher is very easy: just click with your _left_ mouse button to where you want him to move (but there must be a free straight line to it). You can even queue multiple target points forming a _path_ on which Gopher will move along. If you click with the _right_ mouse button, the path will be cleared.
13 |
14 |
15 |
16 | GoLab is written completely in [Go](http://golang.org/), but there is a thin HTML layer because the User Interface (UI) of the game is an HTML page (web page). GoLab doesn't use any platform dependent or native code, so you can start the application on any platforms supported by a Go compiler (including Windows, Linux and MAC OS-X). Since the UI is a simple HTML page, you can play the game from any browsers on any platforms, even from mobile phones and tablets (no HTML5 capable browser is required). Also the device you play from doesn't need to be the same computer where you start the application, so for example you can start the game on your desktop computer and connect to it and play the game from your smart phone. The solution used (web UI server) provides multi-player support out-of-the-box, although this Labyrinth game doesn't make use of it (the same Gopher can be controlled by all clients). Everything is stored in the (Go) application, you can close the browser and reopen it (even on a different device) and nothing will be lost.
17 |
18 | How to get it or install it
19 | ---
20 |
21 | Of course in the _"Go"_ way using `"go get"`:
22 |
23 | `go get github.com/gophergala/golab`
24 |
25 | The executable binary `golab` (produced by `"go install"`) is _self-contained_: it contains all resources embedded (e.g. images, html templates), nothing else is required for it to run. On startup by default the application opens the UI web page in your default browser.
26 |
27 | Configuration and Tweaking
28 | ---
29 |
30 | GoLab can be configured and tweaked through command line parameters or flags. Execute `golab -h` to see the available command line options and their description. For completeness and for those who didn't install GoLab, here is the output:
31 |
32 | Usage of golab:
33 | -autoOpen=true: Auto-opens the UI web page in the default browser
34 | -bulldogs=10: the number of Bulldogs in an area of 1,000 Blocks; valid range: 0..50
35 | -cols=33: the number of columns in the Labyrinth; must be odd; valid range: 9..99
36 | -loopDelay=50: loop delay of the game engine, in milliseconds; valid range: 10..100
37 | -port=1234: Port to start the UI web server on; valid range: 0..65535
38 | -rows=33: the number of rows in the Labyrinth; must be odd; valid range: 9..99
39 | -v=80: moving speed of Gopher and the Bulldogs in pixel/sec; valid range: 20..200
40 | -viewHeight=700: height of the view image in pixels in the UI web page; valid range: 150..2000
41 | -viewWidth=700: width of the view image in pixels in the UI web page; valid range: 150..2000
42 |
43 | Used Packages
44 | ---
45 |
46 | GoLab uses only the standard library that comes with the Official Go distributions. GoLab doesn't rely on any external or 3rd party libraries.
47 |
48 | Used packages from the standard library and their utilisation:
49 |
50 | - [http/net](http://golang.org/pkg/net/http/) package is used as the UI server
51 | - [image](http://golang.org/pkg/image/) package and its sub-packages ([image/color](http://golang.org/pkg/image/color/) and [image/draw](http://golang.org/pkg/image/draw/)) are used to draw the graphics of GoLab
52 | - [image/png](http://golang.org/pkg/image/png/) is used to read image resources of the game
53 | - [image/jpeg](http://golang.org/pkg/image/jpeg/) is used to generate the view of the game (labyrinth) for HTTP clients (browsers)
54 | - [html/template](http://golang.org/pkg/html/template/) package is used to generate the UI web page
55 | - [encoding/base64](http://golang.org/pkg/encoding/base64/) package is used to generate and decode embedded image resources to/form Base64 strings
56 | - [flag](http://golang.org/pkg/flag/) package is used to enable basic configuration through the command line
57 |
58 | Under the Hood (Implementation)
59 | ---
60 |
61 | **Game Engine / Simulation**
62 |
63 | As mentioned earlier, everything is calculated and stored in the (Go) application. As an architectural pattern, I chose [Model-View-Controller (MVC)](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller). Although I did not enforce everything but logically this pattern is followed.
64 |
65 | The `model` package defines the basic types and data structures of the game. The `view` package is responsible for the UI of the game. The UI is a thin HTML layer, it contains an HTML page with some embedded JavaScript. No external JavaScript libraries are used, everything is "self-made". At the GoLab "side" the `net/http` package is used to serve the HTTP clients (browsers).
66 |
67 | The `ctrl` package is the controller or the _engine_ of the game, it implements all the game logic. It runs in an endless loop, and processes events from the UI client(s), performs calculation of moving objects, performs certain checks (like winning and dying) and updates the image / view of the Labyrinth.
68 |
69 | Since there might be multiple goroutines running parallel, communication between the `view` and the `ctrl/model` is done via channels. Also to prevent incomplete/flickering images sent to the clients, the engine performs explicit "model" locking while the next phase of the game is being calculated.
70 |
71 | **Communication between the (Go) application and the browser (UI):**
72 |
73 | - When GoLab is started, it starts an HTTP(web) server.
74 | - Either GoLab auto-opens the UI web page in the default browser (default) or the player manually opens it.
75 | - The UI web page is served by the web server.
76 | - The UI web page presents the view of the game in the form of an HTML image. This image is then periodically refreshed (by JavaScript code).
77 | - Clicks on the view image is detected by JavaScript code and are sent back to the server via AJAX calls. The server processes them.
78 | - Quality is a parameter which is attached to the image urls when the view is requested.
79 | - The FPS parameter is just used at the client side to time image refreshing.
80 | - New Game requests are also sent via AJAX calls.
81 | - The Cheat link opens a new browser tab directed to a URL whose handler sends a snapshot image of the whole Labyrinth.
82 | - The web page constantly monitors the application, and if the application is closed or network error occurs, proper notification/error messages are displayed to the user. The web page automatically "reconnects" if the application becomes available again.
83 | - The web page also automatically detects if the application is restarted, and in this case will reload itself.
84 |
85 | Usefulness
86 | ---
87 |
88 | Since GoLab is a game, its usefulness might be questioned. GoLab's usefulness is that it is an example solution and a reference implementation that you can create portable games or applications with graphics in Go with an implicit portable UI with just using the standard library of Go. GoLab doesn't rely on any external or 3rd party libraries.
89 |
90 | LICENSE
91 | ---
92 |
93 | See [LICENSE](https://github.com/gophergala/golab/blob/master/LICENSE.md)
94 |
95 | GoLab's Gopher is a derivative work based on the Go gopher which was designed by Renee French. ([http://reneefrench.blogspot.com/](http://reneefrench.blogspot.com/)). Licensed under the Creative Commons 3.0 Attributions license.
96 |
97 | The source of other images can be found in the [resources/source.txt](https://github.com/gophergala/golab/blob/master/resources/source.txt) file.
98 |
--------------------------------------------------------------------------------
/ctrl/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Package ctrl implements the game logic.
3 | This can be thought of as the Controller part of an MVC architecture.
4 |
5 | This package, the engine processes events from the clients (received and forwarded by the view package),
6 | and is responsible to calculate the next phases of the game, move objects, and update the image of the Labyrinth.
7 | */
8 | package ctrl
9 |
--------------------------------------------------------------------------------
/ctrl/engine.go:
--------------------------------------------------------------------------------
1 | package ctrl
2 |
3 | import (
4 | "github.com/gophergala/golab/model"
5 | "github.com/gophergala/golab/view"
6 | "image"
7 | "image/draw"
8 | "math"
9 | "math/rand"
10 | "time"
11 | )
12 |
13 | // LoopDelay is the delay between the iterations of the main loop of the game engine, in milliseconds.
14 | var LoopDelay = 50 // ~20 FPS
15 |
16 | // InitNew initializes a new game.
17 | func initNew() {
18 | // Initialize random number generator
19 | rand.Seed(time.Now().Unix())
20 |
21 | model.InitNew()
22 | view.InitNew()
23 | }
24 |
25 | // StartEngine starts the game engine in a new goroutine and returns as soon as possible.
26 | func StartEngine() {
27 | model.NewGameCh <- 1 // Cannot block as application was just started, no incoming requests processed yet
28 |
29 | model.Mutex.Lock()
30 |
31 | go simulate()
32 | }
33 |
34 | // Delta time since our last iteration
35 | var dt float64
36 |
37 | // simulate implements the game cycle
38 | func simulate() {
39 | t := time.Now().UnixNano()
40 |
41 | for {
42 | // Check if we have to start a new game
43 | select {
44 | case <-model.NewGameCh:
45 | initNew()
46 | default:
47 | }
48 |
49 | // First erase target images. We have to do this before handling mouse clicks
50 | // as they may change the target positions
51 | eraseDrawTargetPoss(true)
52 |
53 | // Process mouse clicks
54 | clickLoop:
55 | for {
56 | select {
57 | case click := <-model.ClickCh:
58 | handleClick(click)
59 | default:
60 | break clickLoop
61 | }
62 | }
63 |
64 | // Next clear moving objects from the lab image:
65 | model.Gopher.EraseImg()
66 | for _, bd := range model.Bulldogs {
67 | bd.EraseImg()
68 | }
69 |
70 | model.DrawImgAt(model.ExitImg, model.ExitPos.X, model.ExitPos.X)
71 |
72 | if !model.Dead {
73 | // Draw target positions
74 | eraseDrawTargetPoss(false)
75 | }
76 |
77 | now := time.Now().UnixNano()
78 | dt = float64(now-t) / 1e9
79 |
80 | // Now step moving objects
81 |
82 | stepGopher()
83 | stepBulldogs()
84 |
85 | // Check if Gopher reached the exit point
86 | if int(model.Gopher.Pos.X) == model.ExitPos.X && int(model.Gopher.Pos.Y) == model.ExitPos.Y {
87 | handleWinning()
88 | }
89 |
90 | t = now
91 |
92 | // Sleep some time.
93 | // Iterations might not be exact, but we don't rely on it:
94 | // We calculate delta time and calculate moving and next positions
95 | // based on the delta time.
96 |
97 | model.Mutex.Unlock() // While sleeping, clients can request view images
98 | if model.Won {
99 | // If won, nothing has to be done, just wait for a new game signal
100 | <-model.NewGameCh // Blocking receive
101 | // Send back value to detect it at the proper place
102 | model.NewGameCh <- 1
103 | }
104 | time.Sleep(time.Millisecond * time.Duration(LoopDelay))
105 | model.Mutex.Lock() // We will modify model now, labyrinth image might change so lock.
106 | }
107 | }
108 |
109 | // handleClick handles a mouse click
110 | func handleClick(c model.Click) {
111 | if model.Dead {
112 | return
113 | }
114 |
115 | Gopher := model.Gopher
116 |
117 | if c.Btn == model.MouseBtnRight {
118 | model.TargetPoss = model.TargetPoss[0:0]
119 | }
120 |
121 | // If target buffer is full, do nothing:
122 | if len(model.TargetPoss) == cap(model.TargetPoss) {
123 | return
124 | }
125 |
126 | // Last target pos:
127 | var TargetPos image.Point
128 | if len(model.TargetPoss) == 0 {
129 | TargetPos = Gopher.TargetPos
130 | } else {
131 | TargetPos = model.TargetPoss[len(model.TargetPoss)-1]
132 | }
133 |
134 | // Check if new desired target is in the same row/column as the last target and if there is a free passage to there.
135 | pCol, pRow := TargetPos.X/model.BlockSize, TargetPos.Y/model.BlockSize
136 | tCol, tRow := c.X/model.BlockSize, c.Y/model.BlockSize
137 |
138 | // sorted simply returns its parameters in ascendant order:
139 | sorted := func(a, b int) (int, int) {
140 | if a < b {
141 | return a, b
142 | } else {
143 | return b, a
144 | }
145 | }
146 |
147 | if pCol == tCol { // Same column
148 | for row, row2 := sorted(pRow, tRow); row <= row2; row++ {
149 | if model.Lab[row][tCol] == model.BlockWall {
150 | return // Wall in the route
151 | }
152 | }
153 | } else if pRow == tRow { // Same row
154 | for col, col2 := sorted(pCol, tCol); col <= col2; col++ {
155 | if model.Lab[tRow][col] == model.BlockWall {
156 | return // Wall in the route
157 | }
158 | }
159 | } else {
160 | return // Only the same row or column can be commanded
161 | }
162 |
163 | // Target pos is allowed and reachable.
164 | // Use target position rounded to the center of the target block:
165 | model.TargetPoss = append(model.TargetPoss, image.Pt(tCol*model.BlockSize+model.BlockSize/2, tRow*model.BlockSize+model.BlockSize/2))
166 | }
167 |
168 | // eraseDrawTargetPoss either erases or draws target positions of Gopher, both the current and the buffered ones.
169 | func eraseDrawTargetPoss(erase bool) {
170 | var img image.Image
171 | if erase {
172 | img = model.EmptyImg
173 | } else {
174 | img = model.TargetImg
175 | }
176 | // dtp: drawTargetPos
177 | dtp := func(TargetPos image.Point) {
178 | rect := model.TargetImg.Bounds()
179 | rect = rect.Add(image.Pt(TargetPos.X-rect.Dx()/2, TargetPos.Y-rect.Dy()/2))
180 | draw.Draw(model.LabImg, rect, img, image.Point{}, draw.Over)
181 | }
182 |
183 | dtp(model.Gopher.TargetPos)
184 | for _, TargetPos := range model.TargetPoss {
185 | dtp(TargetPos)
186 | }
187 | }
188 |
189 | // stepGopher handles moving the Gopher and also handles the multiple target positions of Gopher.
190 | func stepGopher() {
191 | Gopher := model.Gopher
192 |
193 | if model.Dead {
194 | Gopher.DrawWithImg(model.DeadImg)
195 | return
196 | }
197 |
198 | // Check if reached current target position:
199 | if int(Gopher.Pos.X) == Gopher.TargetPos.X && int(Gopher.Pos.Y) == Gopher.TargetPos.Y {
200 | // Check if we have more target positions in our path:
201 | if len(model.TargetPoss) > 0 {
202 | // Set the next target as the current
203 | Gopher.TargetPos = model.TargetPoss[0]
204 | // and remove it from the targets:
205 | model.TargetPoss = model.TargetPoss[:copy(model.TargetPoss, model.TargetPoss[1:])]
206 | }
207 | }
208 |
209 | // Step Gopher
210 | stepMovingObj(Gopher)
211 | }
212 |
213 | // stepBulldogs iterates over all Bulldogs, generates new random target if they reached their current, and steps them.
214 | func stepBulldogs() {
215 | // Gopher's position:
216 | gpos := model.Gopher.Pos
217 |
218 | for _, bd := range model.Bulldogs {
219 | x, y := int(bd.Pos.X), int(bd.Pos.Y)
220 |
221 | if bd.TargetPos.X == x && bd.TargetPos.Y == y {
222 | row, col := y/model.BlockSize, x/model.BlockSize
223 | // Generate new, random target.
224 | // For this we shuffle all the directions, and check them sequentially.
225 | // Firts one in which direction there is a free path wins (such path surely exists).
226 |
227 | // Shuffle the directions slice:
228 | for i := len(directions) - 1; i > 0; i-- { // last is already random, no use switching with itself
229 | r := rand.Intn(i + 1)
230 | directions[i], directions[r] = directions[r], directions[i]
231 | }
232 |
233 | var drow, dcol int
234 | for _, dir := range directions {
235 | switch dir {
236 | case model.DirLeft:
237 | dcol = -1
238 | case model.DirRight:
239 | dcol = 1
240 | case model.DirUp:
241 | drow = -1
242 | case model.DirDown:
243 | drow = 1
244 | }
245 | if model.Lab[row+drow][col+dcol] == model.BlockEmpty {
246 | // Direction is good, check if we can even step 2 bocks in this way:
247 | if model.Lab[row+drow*2][col+dcol*2] == model.BlockEmpty {
248 | drow *= 2
249 | dcol *= 2
250 | }
251 | break
252 | }
253 | drow, dcol = 0, 0
254 | }
255 |
256 | bd.TargetPos.X += dcol * model.BlockSize
257 | bd.TargetPos.Y += drow * model.BlockSize
258 | }
259 |
260 | stepMovingObj(bd)
261 |
262 | if !model.Dead {
263 | // Check if this Bulldog reached Gopher
264 | if math.Abs(gpos.X-bd.Pos.X) < model.BlockSize*0.75 && math.Abs(gpos.Y-bd.Pos.Y) < model.BlockSize*0.75 {
265 | handleDying()
266 | }
267 | }
268 | }
269 | }
270 |
271 | // handleDying handles the death of Gopher event.
272 | func handleDying() {
273 | model.Dead = true
274 | }
275 |
276 | // handleWinning handles the winning of game event.
277 | func handleWinning() {
278 | model.Won = true
279 |
280 | r := model.WonImg.Bounds()
281 | r = r.Add(image.Point{view.Pos.X + view.ViewWidth/2 - r.Dx()/2, view.Pos.Y + view.ViewHeight/2 - r.Dy()/2})
282 | draw.Draw(model.LabImg, r, model.WonImg, image.Point{}, draw.Over)
283 | }
284 |
285 | // stepMovingObj steps the specified MovingObj and draws its image to its new position onto the LabImg.
286 | func stepMovingObj(m *model.MovingObj) {
287 | x, y := int(m.Pos.X), int(m.Pos.Y)
288 |
289 | // Only horizontal or vertical movement is allowed!
290 | if x != m.TargetPos.X {
291 | dx := math.Min(dt*model.V, math.Abs(float64(m.TargetPos.X)-m.Pos.X))
292 | if x > m.TargetPos.X {
293 | dx = -dx
294 | m.Direction = model.DirLeft
295 | } else {
296 | m.Direction = model.DirRight
297 | }
298 | m.Pos.X += dx
299 | } else if y != m.TargetPos.Y {
300 | dy := math.Min(dt*model.V, math.Abs(float64(m.TargetPos.Y)-m.Pos.Y))
301 | if y > m.TargetPos.Y {
302 | dy = -dy
303 | m.Direction = model.DirUp
304 | } else {
305 | m.Direction = model.DirDown
306 | }
307 | m.Pos.Y += dy
308 | }
309 |
310 | // Draw image at new position
311 | m.DrawImg()
312 | }
313 |
314 | // directions is a reused slice of all directions
315 | var directions = make([]model.Dir, model.DirLength)
316 |
317 | func init() {
318 | // Populate the directions slice
319 | for i := model.Dir(0); i < model.DirLength; i++ {
320 | directions[i] = i
321 | }
322 | }
323 |
--------------------------------------------------------------------------------
/golab-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/golab-screenshot.png
--------------------------------------------------------------------------------
/golab.go:
--------------------------------------------------------------------------------
1 | // Package main of GoLab is the main package defining the entry point
2 | // and which compiles into the GoLab executable.
3 | //
4 | // Home page: https://github.com/gophergala/golab
5 | package main
6 |
7 | import (
8 | "flag"
9 | "fmt"
10 | "github.com/gophergala/golab/ctrl"
11 | "github.com/gophergala/golab/model"
12 | "github.com/gophergala/golab/view"
13 | "log"
14 | "net/http"
15 | "os/exec"
16 | "runtime"
17 | )
18 |
19 | // port tells on which port to open the UI web server
20 | var port int
21 |
22 | // autoOpen tells if the UI web page should be auto-opened in the users's default browser
23 | var autoOpen bool
24 |
25 | // processFlags registers flags, parses them and validates them.
26 | // Returns nil if everything is ok, else an error.
27 | func processFlags() error {
28 |
29 | // General flags
30 | flag.IntVar(&port, "port", 1234, "Port to start the UI web server on; valid range: 0..65535")
31 | flag.BoolVar(&autoOpen, "autoOpen", true, "Auto-opens the UI web page in the default browser")
32 |
33 | // Model package flags
34 | flag.IntVar(&model.Rows, "rows", 33, "the number of rows in the Labyrinth; must be odd; valid range: 9..99")
35 | flag.IntVar(&model.Cols, "cols", 33, "the number of columns in the Labyrinth; must be odd; valid range: 9..99")
36 | flag.Float64Var(&model.BulldogDensity, "bulldogs", 10, "the number of Bulldogs in an area of 1,000 Blocks; valid range: 0..50")
37 |
38 | // Control/Engine flags
39 | flag.IntVar(&ctrl.LoopDelay, "loopDelay", 50, "loop delay of the game engine, in milliseconds; valid range: 10..100")
40 | flag.Float64Var(&model.V, "v", model.BlockSize*2.0, "moving speed of Gopher and the Bulldogs in pixel/sec; valid range: 20..200")
41 |
42 | // View package flags
43 | flag.IntVar(&view.ViewWidth, "viewWidth", 700, "width of the view image in pixels in the UI web page; valid range: 150..2000")
44 | flag.IntVar(&view.ViewHeight, "viewHeight", 700, "height of the view image in pixels in the UI web page; valid range: 150..2000")
45 |
46 | flag.Parse()
47 |
48 | if port < 0 || port > 65535 {
49 | return fmt.Errorf("port %d is outside of valid range", port)
50 | }
51 |
52 | if model.Rows < 9 || model.Rows > 99 {
53 | return fmt.Errorf("rows %d is outside of valid range", model.Rows)
54 | }
55 |
56 | if model.Cols < 9 || model.Cols > 99 {
57 | return fmt.Errorf("cols %d is outside of valid range", model.Cols)
58 | }
59 |
60 | if model.Rows&0x01 == 0 {
61 | return fmt.Errorf("rows %d must be odd", model.Rows)
62 | }
63 |
64 | if model.Cols&0x01 == 0 {
65 | return fmt.Errorf("cols %d must be odd", model.Cols)
66 | }
67 |
68 | model.LabWidth = model.Cols * model.BlockSize
69 | model.LabHeight = model.Rows * model.BlockSize
70 |
71 | if ctrl.LoopDelay < 10 || ctrl.LoopDelay > 100 {
72 | return fmt.Errorf("loopDelay %d is outside of valid range", ctrl.LoopDelay)
73 | }
74 |
75 | if model.V < 20 || model.V > 200 {
76 | return fmt.Errorf("v %f is outside of valid range", model.V)
77 | }
78 |
79 | if view.ViewWidth < 150 || view.ViewWidth > 2000 {
80 | return fmt.Errorf("viewWidth %d is outside of valid range", view.ViewWidth)
81 | }
82 |
83 | if view.ViewHeight < 150 || view.ViewHeight > 2000 {
84 | return fmt.Errorf("viewHeight %d is outside of valid range", view.ViewHeight)
85 | }
86 |
87 | if model.BulldogDensity < 0 || model.BulldogDensity > 50 {
88 | return fmt.Errorf("bulldogs %f is outside of valid range", model.BulldogDensity)
89 | }
90 |
91 | if view.ViewWidth > model.LabWidth {
92 | fmt.Printf("Warning: viewWidth is trimmed to cols * %d = %d\n", model.BlockSize, model.LabWidth)
93 | view.ViewWidth = model.LabWidth
94 | }
95 |
96 | if view.ViewHeight > model.LabHeight {
97 | fmt.Printf("Warning: viewHeight is trimmed to rows * %d = %d\n", model.BlockSize, model.LabHeight)
98 | view.ViewHeight = model.LabHeight
99 | }
100 |
101 | return nil
102 | }
103 |
104 | // main is the entry point of GoLab.
105 | // Processes the command line flags, initializes the game engine
106 | // and starts the UI webserver.
107 | func main() {
108 | runtime.GOMAXPROCS(runtime.NumCPU())
109 |
110 | if err := processFlags(); err != nil {
111 | fmt.Println(err)
112 | flag.Usage()
113 | return
114 | }
115 |
116 | ctrl.StartEngine()
117 |
118 | fmt.Printf("Starting GoLab webserver on port %d...\n", port)
119 | url := fmt.Sprintf("http://localhost:%d/", port)
120 | if autoOpen {
121 | fmt.Printf("Opening %s...\n", url)
122 | if err := open(url); err != nil {
123 | fmt.Println("Auto-open failed:", err)
124 | fmt.Printf("Open %s in your browser.\n", url)
125 | }
126 | } else {
127 | fmt.Printf("Auto-open not enabled, open %s in your browser.\n", url)
128 | }
129 | log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
130 | }
131 |
132 | // open opens the specified URL in the default browser of the user.
133 | func open(url string) error {
134 | var cmd string
135 | var args []string
136 |
137 | switch runtime.GOOS {
138 | case "windows":
139 | cmd = "cmd"
140 | args = []string{"/c", "start"}
141 | case "darwin":
142 | cmd = "open"
143 | default: // "linux", "freebsd", "openbsd", "netbsd"
144 | cmd = "xdg-open"
145 | }
146 | args = append(args, url)
147 |
148 | return exec.Command(cmd, args...).Start()
149 | }
150 |
--------------------------------------------------------------------------------
/model/const.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | const (
4 | // BlockSize is the size of the labyrinth unit in pixels.
5 | BlockSize = 40
6 | )
7 |
8 | var (
9 | // Rows is the number of rows in the Labyrinth
10 | Rows int
11 | // Cols is the number of columns in the Labyrinth
12 | Cols int
13 |
14 | // LabWidth is the width of the labyrinth's image in pixels.
15 | LabWidth int
16 |
17 | // LabHeight is the height of the labyrinth's image in pixels.
18 | LabHeight int
19 | )
20 |
21 | // V is the moving speed of Gopher and the Buddlogs in pixel/sec.
22 | var V float64
23 |
24 | // "Bulldog density", it tells how many Bulldogs to generate for average of 1,000 blocks.
25 | // For example if this is 10.0 and rows*cols = 21*21 = 441, 10.0*441/1000 = 4.41 => 4 Bulldogs will be generated.
26 | var BulldogDensity float64
27 |
28 | // Type of the unit of the labyrinth
29 | type Block int
30 |
31 | // Block types of the labyrinth
32 | const (
33 | // Empty block (free passage)
34 | BlockEmpty Block = iota
35 | // Wall block
36 | BlockWall
37 | )
38 |
39 | type Dir int
40 |
41 | // Directions of Gopher (facing directions)
42 | const (
43 | DirRight Dir = iota
44 | DirLeft
45 | DirUp
46 | DirDown
47 |
48 | // Not a valid direction: just to tell how many directions are there
49 | DirLength
50 | )
51 |
52 | func (d Dir) String() string {
53 | switch d {
54 | case DirRight:
55 | return "right"
56 | case DirLeft:
57 | return "left"
58 | case DirUp:
59 | return "up"
60 | case DirDown:
61 | return "down"
62 | }
63 | return ""
64 | }
65 |
--------------------------------------------------------------------------------
/model/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Package model defines the basic types and data structures of the game.
3 | This can be thought of as the Model part of an MVC architecture.
4 |
5 | It is "shared", used by the view and ctrl packages.
6 | */
7 | package model
8 |
--------------------------------------------------------------------------------
/model/embedded_imgs.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | // Embedded images mapped from image (file) name to file content encoded in Base64 format.
4 | // Whether these will be used depends on the useEmbeddedImages const in images.go.
5 | var base64Imgs = map[string]string{
6 | "gopher-right.png": "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",
7 | "bulldog-right.png": "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",
8 | "gopher-left.png": "iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAGw0lEQVRYw7WYa1RU1xXH//c5b4YBeQykCkLlIQhWKEWNWusjLtvaZNVUJBpM0yxr0pDSR1pj2q64TJemTTW1aboSiVlJmmqS1kdXsD5iBHRhfARjoyJBZhy5MjA4MzBz53nP6QdtG5YCM4N3fz3/s+9v9tl7nz2HQYLGieKUinUN0wW9rlqXMqFMNJryOVFjBQBKFL8SDvWGvJ7OoMfd5vy0vfnzpn2tAEi832Hi1WdXzSzP/cZ9h0tr16RwogiiKACld3RNyM01XquF7chBtGx6dqn3andT0QPfM1jyCvjzbzcGh6RrwfEATpv19K/fEU1JSe07Xt5QUrvmB1NXrJ4lXfgMesmOMFGgmfE1pGRYQb8AyWm1uLF3Nx4qL0ZuTg6OtLSixZIFS24+Wjdt6Pnqkz9PYwVBDAwMDL21qHoGgM64AQWtbl5NU/PRtKISREMh2I4eQnJxKea57Fg+swoGsxkAIDkceL7pCMjcxWAoBSUEsx0XsXLxwmH+JIcD9cc/gbVyJj59ewdYXsDUB1fh+pmT9P0V3zICkOM6yqV/fuPq2vM2yuv0tKRmNX2iy0Vf3NdER7JHDzTTxy/30tU7/0b9Ph/t6Oi4TbO3+Thdd7GH1tvd1JiZRZ9yDNLHO67T3IVLikcCYUcCNE/KYUkkAqoomPubzYgQgjnWVJjNZuTn59+2ITMwBPACpkVkFBYVoaCgAG1tbcM0RbmTAIZBRJax6A+vQAmHEA0E0H2oyRMvIPlw/Y9fEgwmLNzyEighoACuXL6MwcFB1NfXw+l0Dt+hKGAYBu5wCBqNBjk5ORBFcbjkC7WkTbZAOtWG/Y89dD8AaSRAfqSF3vazW/Y/WnOpZMWqvQAgMADNnoRdu3aBZVlkZGQM0w8kpUAj8HCWVqHj0iXY7HZMnjx5mGbbgcNg772Zm6kFxXh1et76oNezZ9R2Ntqi96otPG/jC/WcIAKE4JzkxKqKMsyqrv6fZtDjwU//0gi7HMae2u+g68i/0OryYMFXysFGIwgFAnB6B/Hsrr+DX7QMJBq5mUMsAzAQHcebXx+NgR9t0ZL35UJtsgVKOAwASJ5SiM3eMNy/+yOKTXpIcgD+0gpwhWU4WfcAhnr74JOugZ1Tja0yj+gNL6gShWg0AbMXgAT+X6hUUZA5vTLrViehCQGml5Zn3BZyQcSEby+H89bmJNmHXw4dxu/3boLT5YVRr0VWmhlvnngDp7/+GJhw6I6+KSFIumeiQWNKYkJDgzTeIgEAiEaT7s4lRMAQAhACMAxs0gBskhs9Xh6ukB52Rx8QCY8SF/z3mLXWiiom4SOmRBnz7mQ0OrxZuQ6/4n3gbwygvLwc393xDtLm14KNhMbaLRgzrUgYMOj1+GPp6oIShicaQN+t1qPheXDMmAEEAF4wGBIH9HR3uWO6dxQFfxqQEdBnYP9Hp2CuqAJVlJhuLE4QEwd0Xfh3f6wDD5uaDsOteSqOEYlhWG50v2M46At63VDNGDBgGWZcgD7pGtQ0hmHHFUFZ7u9TE49NzsmjiQJmPHlfSVXI71czgOxcqWVNdoalLC7A4vx7ZnQe2tq7bfuGtnBUUY2OgEFD3ZJXLx/c2r5kbnlDzIATUpLWppiNOH36IhhRoxogL4p47d2jEHgOwWDEGvM0Y+/pP2jQaewrf/Zy3fTauqeN1ix1MpBSbKypm9lp7939z6Nn3wIQjvdPU+rqDz92Ga3ZqgBSQvBaZeGDEdn/bqJVnGZSCQ4AWJ4Hy3Fp42kzqawoqtoHDemZpoQB06aWWaCyMTzHJgxoSE/Tqw0YdN+QEwbkeIFTE45Eowj7fFLCgEGvN6gmYDQQAK/VnksYsOfjE/0Mw6gHGAqShVu2d42niju8V23qHXEk4jq28Rk6HsD+aydP+FTpgYKAzz/Y0+m1XyHjAcTAxc+eUwPQ39eL41s2/nDMHzKW4NK+93b6rkvAXcxFlufhaD12DMD5u/LCaszMemTlgdYdvOYuTDYMA1fHBbJ72YIvjfZoFHMEAcDXKzW219eeDQVD42NjOVz/5AxyP9juBuCMZU8sjVj3/vaG9s1PLCvLtp3CmfNd4PNLwLAsKIntTZzTaOC1d0Paue1K4706yzfnV+jzJ2bO/8ehU6+POTOOJfjtT2oqeY6bqhAKkUblwL6dv2h8qqF73nMvPJIypXAGJ2om6lInQJtkBisIoJQgIssIDLgQ8fuCEVm+YPvo8LEzr2zbDMD5Xsb3z61YOmtav3tw9t3K6eStzzzs+euLP/ICSB9BkwdgMYDlAJYBqAKgHcnh+rX3t8ypLHo4lo//B55rzuqdnhrKAAAAAElFTkSuQmCC",
9 | "bulldog-left.png": "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",
10 | "gopher-up.png": "iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAEdklEQVRYw7XYfUxVZRwH8O95vRfuC3B5vYAEg4wQBQJDxQ1WyWTVWi0twDVgy96WW2zmRlSbzLasLS3nXKFzjVbD2sxaODReAs0UBSQhAkQQLpc34cLl3nNfzjn9of3nvdx7Ds/33/PcPZ8993l+5/ccCgrD8Pz6vLerc7jQkK0hpqgsXm9IY3iNGQBkSVwR3S6ry7Y4JCwuXJm+2fPHcNO5TgBSsPNQwY5PyN+WnfL0zosbyytNDMdDkkRAln3/gKZBMyxGfz+PjkMfPWsbH216/KVXdBGpj7F9350Sli0TghrgpoIDH3/PG4zGnpPHazPLK1/PeLmsgNFq/aIePhMF2etF5ycfTj657/1omuN45/z8ckPx1lwAQ0EDOW1IUWlTR6txXRJomsFYRwuSCgohejxQE4bjcauxARt274EkemHt7pJ/evV5PQDHw8bTvuDFX5z41pi4DpLbjd7TXyNxy3bVOAAQPW5k7CrDaGszZElCXE4elbKjJNnXeJ/AsEeSaVkUsWyZRGZ5BWRJwlpF8nrBh+rA8Bp4nU6MXmhaDBYotdS89yUoCrP9N9cU93+0pkhYrv2JX/bueRGAxeeW8PXAbp26NN17ozt2U3ZpeEramgN1sWb88FxRzcLtoXp/42h/D8c7WvsStmwHiciSiCfeeLdktXF+gRGpj6ZrwyPIAEURcTmb41crdX6BMRuzY0EosiTBmJik0xiMyoG83hACgqFoSmvOy1cOlCVRAtFQnD7OrHwPCrbFFbJAsJxOpxy4ODqyQBhIMRyvHDjX//ds8A1PkC0EzSgHApgRbAQXkQIFmqJUAe2WCRBeQlUr6HDMzhAtNOHJqbJSYOy+nZn5rhWiB5kutHRUJsRGZAUFzEhLzB26cMR69FjtFbdXJKaTQKG6ouSbf5uP9JQUZlcHDIwyGd80henR1TUAiteQK4I8j/ozreBYBoLgMQfcbo1NzjbrQjRjZfuPV+SUVxzQm+PJ7EBZRl1pxbahMWvjr603GgC4g700Rb7WcnVOb04g1jDUb07f7XGsnFF6iqMNhHAAQLMsaIaJVlNmImmeJ1oHdTFxBsXA6A1ZESAcimVoxUBdTHQoaaCwcM+hGMiwHEMSJ3m9cNvtFhX9oE0gCfQ6nWC12l7FwMmrl2cpily75XUJ0o7Dx0bUnOJB2/gdcn+xxzPXXveBrAY4O/HXZTuRGshxGP7t7JBt7LakBoj5gVsHSQBXZqy4dLjuLVUXdwD459yPp+1TFmAN9yLNsrjb2d4OoG/1pjuA6OPiq8rOd55kNWvQ2VAU5gb75cYXnkn099Eo4BW8/yHJcqrrndJrLpdbZf/MYKr7OoSvatoDwQUMBIC9xZlh++81wdt29v5LnmUDhjEaDZYmxjH4ee3oIcMA9Fru58DvVYEnvGrXU7XC0tLdxra+kaKDn1WZ1qfnMrwmKSQyClpjGGiOgyxL8DgccM7PwbNiFzwOR/+dtovt108c/RTANIA0AMMkgP6S+mBi44Om0wqgF4DqN9F/rNaaIny43Z0AAAAASUVORK5CYII=",
11 | "bulldog-up.png": "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",
12 | "gopher-down.png": "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",
13 | "bulldog-down.png": "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",
14 | "wall.png": "iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAABG0lEQVRYw+3Xva5EQBwF8EMYIgqtB+AFNqLUexS111OIRkJLKZFoJQoRJj62sDfXtZtbjdXMaRDJ/PKfjMQBbooAIAgC3/cNw5im6WpP1/UkScIwlAAYhmFZlm3b35xYBFBVVRRFcRwPw/AFsu97ABIARVHats2yDIDruoSQdV2Ze5Ik/XncL7Is932fpikA5vC6roSQx+NxtKXja0oppXS/Zwt/2IDf8y0I4k/YH6W3NcW7vmMOc5jDHOYwhznMYQ5zmMMMqtT7L/9FTeK07Bm+rjtRSlVV/QCLojjPc57nzMfd2yIAx3E0TftvYrbZtm1Zlr1/e56nKMoLJoQcN+GijONYlqVpml3XveC6ri+a9ZSiKJqm2eHb8gQldHKbOjeEqgAAAABJRU5ErkJggg==",
15 | "gopher-dead.png": "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",
16 | "door.png": "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",
17 | "marker.png": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACL0lEQVQ4y32ST0gVQRzHPzu7+54+s+wPmWnQRahOQV0KikKqQxHUpboU0SWyg0QEXoQiuhRG6EWwTkEQJEQkiRfBoKA6aUQGgYWapD3T3bc7M7szHXoPHq+1HwwMM7/Ph+/8ccio4dkzu7fW7+9W5vceg7F1TtP7b+Ho7ZPbXn6s7XVqF0ZnL3QL4d0JzA/yXhPGaOJkgUbRhrGm61jr4werCoamO446DiOOcKnPNdO0thWdBiwtzxDrBRzjonR68Fz7+HiFEdWCWC/fl2YJMEy+nafz8BBdR0aY+lAEDLEpotKgt5qpEQS7rEkJgxK9lz6xPJ8SLAj6r8yglCRNDZEO9lYzXmVy702LJ7XCdQVzn1fI5328XA7f9/FcwdxXxbo2jdaazATX980lSus4lpKWnZZCYz2+7+Pncqzf1MCG7QqpJFLrNDMBgJTqobV0Jibl6qNmJp5vwc+57DgxTRDGSKVRSj1d9RVujW1s8Fw38H0P1xUIYbBYTOKSpAlKJSSp3XyzY/Fn5iX2HFoMY6muhVFMqRQThppSqAmjiDCSxEp1VsOZHwngxnBhyhWi3XEcrLUYazDGvr57PDpQ2+tlCSIpTwMTFbvBMvnKnhUudSZFA6a8ZUWWoO9UOunAC5y/GeMlp39swCYmpQEoALlK+kwBgFxxLvueh+d6dvBi2gesKcP5am5VwcD5ZNYV7pPidzEI1AES+FUeUeUYHv+puCh63j2TBeBLGfqn/gA1mwntfiET+gAAAABJRU5ErkJggg==",
18 | "won.png": "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"}
19 |
--------------------------------------------------------------------------------
/model/images.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "bytes"
5 | "encoding/base64"
6 | "fmt"
7 | "image"
8 | "image/color"
9 | "image/draw"
10 | "image/png"
11 | "io/ioutil"
12 | )
13 |
14 | // Tells if the embedded images are to be used. If false, images from files will be loaded.
15 | const useEmbeddedImages = true
16 |
17 | // Image of the labyrinth
18 | var LabImg *image.RGBA
19 |
20 | // Gopher images for each direction, each has zero Min point
21 | var GopherImgs []*image.RGBA = make([]*image.RGBA, DirLength)
22 |
23 | // Dead Gopher image.
24 | var DeadImg *image.RGBA
25 |
26 | // Bulldog images for each direction, each has zero Min point
27 | var BulldogImgs []*image.RGBA = make([]*image.RGBA, DirLength)
28 |
29 | // Image of the wall block
30 | //var WallImg = image.NewUniform(WallCol)
31 | var WallImg *image.RGBA
32 |
33 | // Image of the empty block
34 | var EmptyImg = image.NewUniform(color.RGBA{A: 0xff})
35 |
36 | // Image of the empty block
37 | var TargetImg *image.RGBA
38 |
39 | // Image of a door, this is the exit sign
40 | var ExitImg *image.RGBA
41 |
42 | // Image of a congratulation
43 | var WonImg *image.RGBA
44 |
45 | func init() {
46 | for i := Dir(0); i < DirLength; i++ {
47 | // Load Gopher images
48 | GopherImgs[i] = loadImg(fmt.Sprintf("gopher-%s.png", i), true)
49 | // Load Bulldog images
50 | BulldogImgs[i] = loadImg(fmt.Sprintf("bulldog-%s.png", i), true)
51 | }
52 |
53 | WallImg = loadImg("wall.png", true)
54 | DeadImg = loadImg("gopher-dead.png", true)
55 | ExitImg = loadImg("door.png", true)
56 |
57 | TargetImg = loadImg("marker.png", false)
58 | WonImg = loadImg("won.png", false)
59 | }
60 |
61 | // loadImg loads a PNG image from the specified file, and converts it to image.RGBA and makes sure image has zero Min point.
62 | // This function only used during development as the result contains the images embedded.
63 | // blockSize tells if the image must be of the size of a block (else panics).
64 | func loadImg(name string, blockSize bool) *image.RGBA {
65 | var data []byte
66 | var err error
67 |
68 | if useEmbeddedImages {
69 | data, err = base64.StdEncoding.DecodeString(base64Imgs[name])
70 | } else {
71 | data, err = ioutil.ReadFile(name)
72 | }
73 | if err != nil {
74 | panic(err)
75 | }
76 | return decodeImg(data, blockSize)
77 | }
78 |
79 | // decodeImg decodes an image from the specified data which must be of PNG format.
80 | // blockSize tells if the image must be of the size of a block (else panics).
81 | func decodeImg(data []byte, blockSize bool) *image.RGBA {
82 | src, err := png.Decode(bytes.NewBuffer(data))
83 | if err != nil {
84 | panic(err)
85 | }
86 |
87 | // Convert to image.RGBA, also make sure result image has zero Min point
88 | b := src.Bounds()
89 | if blockSize && (b.Dx() != BlockSize || b.Dy() != BlockSize) {
90 | panic("Invalid image size!")
91 | }
92 |
93 | img := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy()))
94 | draw.Draw(img, src.Bounds(), src, b.Min, draw.Src)
95 |
96 | return img
97 | }
98 |
99 | // printBase64Imgs prints the Base64 encoded strings of the images.
100 | // The printed text is a valid go source format created a map with file names mapped to their base64 encoded contents.
101 | // Used only during development to include those Base64 strings here in the source file
102 | // in order to embed them in the executable native binary.
103 | func printBase64Imgs() {
104 | var names []string
105 | for i := Dir(0); i < DirLength; i++ {
106 | // Gopher images
107 | names = append(names, fmt.Sprintf("gopher-%s.png", i))
108 | // Bulldog images
109 | names = append(names, fmt.Sprintf("bulldog-%s.png", i))
110 | }
111 |
112 | names = append(names, "wall.png")
113 | names = append(names, "gopher-dead.png")
114 | names = append(names, "door.png")
115 | names = append(names, "marker.png")
116 | names = append(names, "won.png")
117 |
118 | // Generate output
119 | fmt.Print("var base64Imgs = map[string]string{")
120 | for i, name := range names {
121 | data, err := ioutil.ReadFile(name)
122 | if err != nil {
123 | panic(err)
124 | }
125 |
126 | if i > 0 {
127 | fmt.Print(",")
128 | }
129 |
130 | fmt.Printf("\n\t\"%s\": \"%s\"", name, base64.StdEncoding.EncodeToString(data))
131 | }
132 | fmt.Print("}")
133 | }
134 |
--------------------------------------------------------------------------------
/model/lab.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "image"
5 | "image/draw"
6 | "math/rand"
7 | "sync"
8 | )
9 |
10 | // Mutex to be used to synchronize model modifications
11 | var Mutex sync.Mutex
12 |
13 | // The model/data of the labyrinth
14 | var Lab [][]Block
15 |
16 | // MovingObj is a struct describing a moving object.
17 | type MovingObj struct {
18 | // The position in the labyrinth in pixel coordinates
19 | Pos struct {
20 | X, Y float64
21 | }
22 |
23 | // Direction where the object is facing toward
24 | Direction Dir
25 |
26 | // Target position the object is moving to
27 | TargetPos image.Point
28 |
29 | // Images for each direction, each has zero Min point
30 | Imgs []*image.RGBA
31 | }
32 |
33 | // DrawImg draws the image of the MovingObj to the LabImg.
34 | func (m *MovingObj) DrawImg() {
35 | m.DrawWithImg(m.Imgs[m.Direction])
36 | }
37 |
38 | // EraseImg erases the image of the MovingObj from the LabImg by drawing empty block to it.
39 | func (m *MovingObj) EraseImg() {
40 | m.DrawWithImg(EmptyImg)
41 | }
42 |
43 | // DrawWithImage draws the specified image at the position of the moving object onto the LabImg.
44 | func (m *MovingObj) DrawWithImg(img image.Image) {
45 | DrawImgAt(img, int(m.Pos.X), int(m.Pos.Y))
46 | }
47 |
48 | // DrawImgAt draws the specified image at the specified position which specifies the center of the area to draw.
49 | // The size of the image draw is the block size.
50 | func DrawImgAt(img image.Image, x, y int) {
51 | r := image.Rect(0, 0, BlockSize, BlockSize).Add(image.Point{x - BlockSize/2, y - BlockSize/2})
52 | draw.Draw(LabImg, r, img, image.Point{}, draw.Over)
53 | }
54 |
55 | // Gopher is our hero, the moving object the user can control.
56 | var Gopher = new(MovingObj)
57 |
58 | // Dead tells if Gopher died
59 | var Dead bool
60 |
61 | // Tells if we won
62 | var Won bool
63 |
64 | // For Gopher we maintain multiple target positions which define a path on which Gopher will move along
65 | var TargetPoss = make([]image.Point, 0, 20)
66 |
67 | // Slice of Bulldogs, the ancient enemy of Gophers.
68 | var Bulldogs []*MovingObj
69 |
70 | // Exit position
71 | var ExitPos = image.Point{}
72 |
73 | // Channel to signal new game
74 | var NewGameCh = make(chan int, 1)
75 |
76 | // Constant for the right Mouse button value in the Click struct.
77 | // Button value for left and middle may not be the same for older browsers, but right button always has this value.
78 | const MouseBtnRight = 2
79 |
80 | // Click describes a mouse click.
81 | type Click struct {
82 | // X, Y are the mouse coordinates in pixel, in the coordinate system of the Labyrinth
83 | X, Y int
84 | // Btn is the mouse button
85 | Btn int
86 | }
87 |
88 | // Channel to receive mouse clicks on (view package sends, ctrl package (engine) processes them)
89 | var ClickCh = make(chan Click, 10)
90 |
91 | // InitNew initializes a new game.
92 | func InitNew() {
93 | LabImg = image.NewRGBA(image.Rect(0, 0, LabWidth, LabHeight))
94 |
95 | Bulldogs = make([]*MovingObj, int(float64(Rows*Cols)*BulldogDensity/1000))
96 |
97 | Dead = false
98 | Won = false
99 |
100 | initLab()
101 |
102 | initGopher()
103 |
104 | initBulldogs()
105 |
106 | initLabImg()
107 |
108 | ExitPos.X, ExitPos.Y = (Cols-2)*BlockSize+BlockSize/2, (Rows-2)*BlockSize+BlockSize/2
109 | }
110 |
111 | // initLab initializes and generates a new Labyrinth.
112 | func initLab() {
113 | Lab = make([][]Block, Rows)
114 | for i := range Lab {
115 | Lab[i] = make([]Block, Cols)
116 | }
117 |
118 | // Zero value of the labyrinth is full of empty blocks
119 |
120 | // generate labyrinth
121 | genLab()
122 | }
123 |
124 | // initGopher initializes Gopher.
125 | func initGopher() {
126 | // Position Gopher to top left corner
127 | Gopher.Pos.X = BlockSize + BlockSize/2
128 | Gopher.Pos.Y = Gopher.Pos.X
129 | Gopher.Direction = DirRight
130 | Gopher.TargetPos.X, Gopher.TargetPos.Y = int(Gopher.Pos.X), int(Gopher.Pos.Y)
131 | Gopher.Imgs = GopherImgs
132 |
133 | // Throw away queued targets
134 | TargetPoss = TargetPoss[0:0]
135 | }
136 |
137 | // initBulldogs creates and initializes the Bulldogs.
138 | func initBulldogs() {
139 | for i := 0; i < len(Bulldogs); i++ {
140 | bd := new(MovingObj)
141 | Bulldogs[i] = bd
142 |
143 | // Place bulldog at a random position
144 | var row, col = int(Gopher.Pos.Y) / BlockSize, int(Gopher.Pos.X) / BlockSize
145 | // Give some space to Gopher: do not generate Bulldogs too close:
146 | for gr, gc := row, col; (row-gr)*(row-gr) <= 16 && (col-gc)*(col-gc) <= 16; row, col = rPassPos(0, Rows), rPassPos(0, Cols) {
147 | }
148 |
149 | bd.Pos.X = float64(col*BlockSize + BlockSize/2)
150 | bd.Pos.Y = float64(row*BlockSize + BlockSize/2)
151 |
152 | bd.TargetPos.X, bd.TargetPos.Y = int(bd.Pos.X), int(bd.Pos.Y)
153 | bd.Imgs = BulldogImgs
154 | }
155 | }
156 |
157 | // initLabImg initializes and draws the image of the Labyrinth.
158 | func initLabImg() {
159 | // Clear the labyrinth image
160 | draw.Draw(LabImg, LabImg.Bounds(), EmptyImg, image.Pt(0, 0), draw.Over)
161 |
162 | // Draw walls
163 | zeroPt := image.Point{}
164 | for ri, row := range Lab {
165 | for ci, block := range row {
166 | if block == BlockWall {
167 | x, y := ci*BlockSize, ri*BlockSize
168 | rect := image.Rect(x, y, x+BlockSize, y+BlockSize)
169 | draw.Draw(LabImg, rect, WallImg, zeroPt, draw.Over)
170 | }
171 | }
172 | }
173 | }
174 |
175 | // genLab generates a random labyrinth.
176 | func genLab() {
177 | // Create a "frame":
178 | for ri := range Lab {
179 | Lab[ri][0] = BlockWall
180 | Lab[ri][Cols-1] = BlockWall
181 | }
182 | for ci := range Lab[0] {
183 | Lab[0][ci] = BlockWall
184 | Lab[Rows-1][ci] = BlockWall
185 | }
186 |
187 | genLabArea(0, 0, Rows-1, Cols-1)
188 | }
189 |
190 | // genLabArea generates a random labyrinth inside the specified area, borders exclusive.
191 | // This is a recursive implementation, each iteration divides the area into 2 parts.
192 | func genLabArea(x1, y1, x2, y2 int) {
193 | dx, dy := x2-x1, y2-y1
194 |
195 | // Exit condition from the recursion:
196 | if dx <= 2 || dy <= 2 {
197 | return
198 | }
199 |
200 | // Decide if we do a veritcal or horizontal split
201 | var vert bool
202 | if dy > dx {
203 | vert = false
204 | } else if dx > dy {
205 | vert = true
206 | } else if rand.Intn(2) == 0 { // Area is square, choose randomly
207 | vert = true
208 | }
209 |
210 | if vert {
211 | // Add vertical split
212 | var x int
213 | if dx > 6 { // To avoid long straight paths, only use random in smaller areas
214 | x = midWallPos(x1, x2)
215 | } else {
216 | x = rWallPos(x1, x2)
217 | }
218 | // A whole in it:
219 | y := rPassPos(y1, y2)
220 | for i := y1; i <= y2; i++ {
221 | if i != y {
222 | Lab[i][x] = BlockWall
223 | }
224 | }
225 |
226 | genLabArea(x1, y1, x, y2)
227 | genLabArea(x, y1, x2, y2)
228 | } else {
229 | // Add horizontal split
230 | var y int
231 | if dy > 6 { // To avoid long straight paths, only use random in smaller areas
232 | y = midWallPos(y1, y2)
233 | } else {
234 | y = rWallPos(y1, y2)
235 | }
236 | // A whole in it:
237 | x := rPassPos(x1, x2)
238 | for i := x1; i <= x2; i++ {
239 | if i != x {
240 | Lab[y][i] = BlockWall
241 | }
242 | }
243 |
244 | genLabArea(x1, y1, x2, y)
245 | genLabArea(x1, y, x2, y2)
246 | }
247 | }
248 |
249 | // rWallPos returns a random wall position which is an even number between the specified min and max.
250 | func rWallPos(min, max int) int {
251 | return min + (rand.Intn((max-min)/2-1)+1)*2
252 | }
253 |
254 | // midWallPos returns the wall position being at the middle of the specified min and max.
255 | func midWallPos(min, max int) int {
256 | n := (min + max) / 2
257 | // make sure it's even
258 | if n&0x01 == 1 {
259 | n--
260 | }
261 | return n
262 | }
263 |
264 | // rPassPos returns a random passage position which is an odd number between the specified min and max.
265 | func rPassPos(min, max int) int {
266 | return rWallPos(min, max+2) - 1
267 | }
268 |
--------------------------------------------------------------------------------
/resources/bulldog-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/resources/bulldog-down.png
--------------------------------------------------------------------------------
/resources/bulldog-left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/resources/bulldog-left.png
--------------------------------------------------------------------------------
/resources/bulldog-right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/resources/bulldog-right.png
--------------------------------------------------------------------------------
/resources/bulldog-up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/resources/bulldog-up.png
--------------------------------------------------------------------------------
/resources/door.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/resources/door.png
--------------------------------------------------------------------------------
/resources/gopher-dead.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/resources/gopher-dead.png
--------------------------------------------------------------------------------
/resources/gopher-down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/resources/gopher-down.png
--------------------------------------------------------------------------------
/resources/gopher-left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/resources/gopher-left.png
--------------------------------------------------------------------------------
/resources/gopher-right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/resources/gopher-right.png
--------------------------------------------------------------------------------
/resources/gopher-up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/resources/gopher-up.png
--------------------------------------------------------------------------------
/resources/marker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/resources/marker.png
--------------------------------------------------------------------------------
/resources/source.txt:
--------------------------------------------------------------------------------
1 | These resources are not required at runtime, they are embedded into the executable binary.
2 | Whether the embedded images are used is controlled by the useEmbeddedImages constant/variable in model/images.go file.
3 | If it is set to false, these images are searched for in the current directory.
4 |
5 |
6 | Original Gopher image from:
7 | https://github.com/golang-samples/gopher-vector
8 |
9 | Original Bulldog images from:
10 | http://www.3dcadbrowser.com/download.aspx?3dmodel=14738
11 |
12 | Wall image from:
13 | http://gamedesign.wdfiles.com/local--files/spriteart%3Asprite-art-101-basic-depth/sprite_art_depth_tutorial_1.png
14 |
15 | Target image (marker):
16 | https://www.iconfinder.com/icons/73052/base_biswajit_chartreuse_con_map_marker_outside_pixe_icon
17 |
18 | Door (Exit sign) image from:
19 | http://www.fengshuiatwork.com/blog/wp-content/uploads/2011/09/front-door.png
20 |
21 | Congratulations image from:
22 | http://www.wetumpkaband.com/images/Congratulations.png
23 |
24 |
--------------------------------------------------------------------------------
/resources/wall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/resources/wall.png
--------------------------------------------------------------------------------
/resources/won.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gophergala/golab/6c50e3f612dc324fa36a21e61516988b1eca81ba/resources/won.png
--------------------------------------------------------------------------------
/view/const.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | // Title as the application - as it appears in the Browser title
4 | const AppTitle = "Gopher's Labyrinth - First Gopher Gala (2015)"
5 |
6 | var (
7 | // Width of the client view in pixels
8 | ViewWidth int
9 | // Width of the client view in pixels
10 | ViewHeight int
11 | )
12 |
--------------------------------------------------------------------------------
/view/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Package view is responsible for the UI of the game.
3 | This can be thought of as the View part of an MVC architecture.
4 |
5 | The UI is a thin HTML layer, it contains an HTML page with some embedded JavaScript.
6 | No external JavaScript libraries are used, everything is "self-made".
7 |
8 | The net/http package is used to serve the HTTP clients (browsers).
9 | */
10 | package view
11 |
--------------------------------------------------------------------------------
/view/handlers.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "fmt"
5 | "github.com/gophergala/golab/model"
6 | "html/template"
7 | "image"
8 | "image/jpeg"
9 | "net/http"
10 | "strconv"
11 | "time"
12 | )
13 |
14 | var Params = struct {
15 | Title string
16 | Width, Height *int
17 | RunId int64
18 | ShowFreezeBtn bool
19 | }{AppTitle, &ViewWidth, &ViewHeight, time.Now().Unix(), false}
20 |
21 | // Template of the play html page
22 | var playTempl = template.Must(template.New("t").Parse(play_html))
23 |
24 | // Template of the help html page
25 | var helpTempl = template.Must(template.New("t").Parse(help_html))
26 |
27 | // The client's (browser's) view position inside the Labyrinth image. This is the top-left point of the view.
28 | var Pos image.Point
29 |
30 | // init registers the http handlers.
31 | func init() {
32 | http.HandleFunc("/", playHtmlHandle)
33 | http.HandleFunc("/runid", runIdHandle)
34 | http.HandleFunc("/img", imgHandle)
35 | http.HandleFunc("/clicked", clickedHandle)
36 | http.HandleFunc("/cheat", cheatHandle)
37 | http.HandleFunc("/new", newGameHandle)
38 | http.HandleFunc("/help", helpHtmlHandle)
39 | }
40 |
41 | // InitNew initializes a new game.
42 | func InitNew() {
43 | Pos = image.Point{}
44 | }
45 |
46 | // playHtmlHandle serves the html page where the user can play.
47 | func playHtmlHandle(w http.ResponseWriter, r *http.Request) {
48 | playTempl.Execute(w, Params)
49 | }
50 |
51 | // runidHandle serves the running app id which changes if app is restarted
52 | // (so browser clients can detect if app was restarted).
53 | func runIdHandle(w http.ResponseWriter, r *http.Request) {
54 | fmt.Fprintf(w, "%d", Params.RunId)
55 | }
56 |
57 | // imgHandle serves images of the player's view.
58 | func imgHandle(w http.ResponseWriter, r *http.Request) {
59 | quality, err := strconv.Atoi(r.FormValue("quality"))
60 | if err != nil || quality < 0 || quality > 100 {
61 | quality = 70
62 | }
63 |
64 | // Center Gopher in view if possible
65 | gpos := model.Gopher.Pos
66 | rect := image.Rect(0, 0, ViewWidth, ViewHeight).Add(image.Pt(int(gpos.X)-ViewWidth/2, int(gpos.Y)-ViewHeight/2))
67 |
68 | // But needs correction at the edges of the view (it can't be centered)
69 | corr := image.Point{}
70 | if rect.Min.X < 0 {
71 | corr.X = -rect.Min.X
72 | }
73 | if rect.Min.Y < 0 {
74 | corr.Y = -rect.Min.Y
75 | }
76 | if rect.Max.X > model.LabWidth {
77 | corr.X = model.LabWidth - rect.Max.X
78 | }
79 | if rect.Max.Y > model.LabHeight {
80 | corr.Y = model.LabHeight - rect.Max.Y
81 | }
82 | rect = rect.Add(corr)
83 |
84 | model.Mutex.Lock()
85 | jpeg.Encode(w, model.LabImg.SubImage(rect), &jpeg.Options{quality})
86 | model.Mutex.Unlock()
87 |
88 | // Store the new view's position:
89 | Pos = rect.Min
90 | }
91 |
92 | // clickedHandle receives mouse click (mouse button pressed) events with mouse coordinates.
93 | func clickedHandle(w http.ResponseWriter, r *http.Request) {
94 | x, err := strconv.Atoi(r.FormValue("x"))
95 | if err != nil {
96 | return
97 | }
98 | y, err := strconv.Atoi(r.FormValue("y"))
99 | if err != nil {
100 | return
101 | }
102 | btn, err := strconv.Atoi(r.FormValue("b"))
103 | if err != nil {
104 | return
105 | }
106 |
107 | // x, y are in the coordinate system of the client's view.
108 | // Translate them to the Labyrinth's coordinate system:
109 | select {
110 | case model.ClickCh <- model.Click{Pos.X + x, Pos.Y + y, btn}:
111 | default:
112 | }
113 | }
114 |
115 | // cheatHandle serves the whole image of the Labyrinth.
116 | func cheatHandle(w http.ResponseWriter, r *http.Request) {
117 | model.Mutex.Lock()
118 | jpeg.Encode(w, model.LabImg, &jpeg.Options{70})
119 | model.Mutex.Unlock()
120 | }
121 |
122 | // newGameHandle signals to start a newgame.
123 | func newGameHandle(w http.ResponseWriter, r *http.Request) {
124 | // Use non-blocking send
125 | select {
126 | case model.NewGameCh <- 1:
127 | default:
128 | }
129 | }
130 |
131 | // helpHtmlHandle serves the help html page.
132 | func helpHtmlHandle(w http.ResponseWriter, r *http.Request) {
133 | helpTempl.Execute(w, Params)
134 | }
135 |
--------------------------------------------------------------------------------
/view/help_html.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | const help_html = `
4 |
5 | {{.Title}}
6 |
12 |
13 |
14 |
15 |
16 |
{{.Title}}
17 |
18 |
19 |
20 | Gopher's Labyrinth (or just GoLab) is a 2-dimensional Labyrinth game where you control
21 | Gopher (who else)
22 | and your goal is to get to the Exit point of the Labyrinth. But beware of the bloodthirsty Bulldogs,
23 | the ancient enemies of gophers who are endlessly roaming the Labyrinth!
24 |
25 |
26 | Controlling Gopher is very easy: just click with your left mouse button to where you want him to move
27 | (but there must be a free straight line to it). You can even queue multiple target points forming a path
28 | on which Gopher will move along. If you click with the right mouse button, the path will be cleared.
29 |