├── .gitignore ├── README.md ├── controller.go ├── game.go ├── levels_db.go ├── render.go ├── screenshot.png └── sokoban_levels.txt /.gitignore: -------------------------------------------------------------------------------- 1 | sokoban 2 | *sokoban-go 3 | *.zip 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sokoban In Terminal With Go 2 | 3 | ![alt text](https://raw.githubusercontent.com/rn2dy/sokoban-go/master/screenshot.png "screen shot") 4 | 5 | ## Setup 6 | ``` 7 | git clone github.com/rn2dy/sokoban-go 8 | cd path/to/sokoban-go 9 | go get -u github.com/nsf/termbox-go 10 | go build -o sokoban 11 | ./sokoban 12 | ``` 13 | 14 | ## Credit 15 | 16 | Inspired by [gotetris](https://github.com/jjinux/gotetris) 17 | -------------------------------------------------------------------------------- /controller.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/nsf/termbox-go" 5 | "time" 6 | ) 7 | 8 | const animationSpeed = 10 * time.Millisecond 9 | 10 | func main() { 11 | err := termbox.Init() 12 | if err != nil { 13 | panic(err) 14 | } 15 | defer termbox.Close() 16 | 17 | eventQueue := make(chan termbox.Event) 18 | go func() { 19 | for { 20 | eventQueue <- termbox.PollEvent() 21 | } 22 | }() 23 | 24 | g := NewGame() 25 | render(g) 26 | 27 | for { 28 | ev := <-eventQueue 29 | if ev.Type == termbox.EventKey { 30 | switch { 31 | case ev.Key == termbox.KeyArrowUp || ev.Ch == 'k': 32 | g.move(UP) 33 | case ev.Key == termbox.KeyArrowDown || ev.Ch == 'j': 34 | g.move(DOWN) 35 | case ev.Key == termbox.KeyArrowLeft || ev.Ch == 'h': 36 | g.move(LEFT) 37 | case ev.Key == termbox.KeyArrowRight || ev.Ch == 'l': 38 | g.move(RIGHT) 39 | case ev.Ch == 'n': 40 | g.nextLevel() 41 | case ev.Ch == 'p': 42 | g.prevLevel() 43 | case ev.Ch == 'r': 44 | g.reset() 45 | case ev.Ch == 'd': 46 | g.toggleDebug() 47 | case ev.Key == termbox.KeyEsc: 48 | return 49 | } 50 | } 51 | render(g) 52 | time.Sleep(animationSpeed) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /game.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "fmt" 5 | ) 6 | 7 | type Direction int 8 | 9 | const ( 10 | UP Direction = iota 11 | DOWN 12 | LEFT 13 | RIGHT 14 | ) 15 | 16 | var baseTokens = []byte("#@XO* ") 17 | 18 | var ( 19 | WALL = baseTokens[0] 20 | GUY = baseTokens[1] 21 | SLOT = baseTokens[2] 22 | BOX = baseTokens[3] 23 | BOXIN = baseTokens[4] 24 | FLOOR = baseTokens[5] 25 | ) 26 | 27 | type Cell struct { 28 | base, obj byte 29 | } 30 | 31 | type Game struct { 32 | level int 33 | board [][]Cell 34 | db *ldb 35 | x int 36 | y int 37 | debug bool 38 | } 39 | 40 | func NewGame() *Game { 41 | g := &Game{ 42 | level: 1, 43 | db: &ldb{}, 44 | } 45 | g.db.loadAll() 46 | g.reset() 47 | return g 48 | } 49 | 50 | // reset current level 51 | func (g *Game) reset() { 52 | g.board = g.db.getLevel(g.level) 53 | g.whereami() 54 | } 55 | 56 | func (g *Game) whereami() { 57 | for y, cels := range g.board { 58 | for x, cel := range cels { 59 | if cel.obj == GUY { 60 | g.x, g.y = x, y 61 | } 62 | } 63 | } 64 | } 65 | 66 | func (g *Game) checkMove(dx, dy int) { 67 | var ( 68 | x, y int 69 | k = 1 70 | ) 71 | for { 72 | x, y = g.x+dx*k, g.y+dy*k 73 | cell := g.board[y][x] 74 | switch cell.obj { 75 | case FLOOR, SLOT: 76 | // move obj along the way foward 77 | for k > 0 { 78 | xp, yp := x-dx, y-dy 79 | g.board[y][x].obj = g.board[yp][xp].obj 80 | x, y = xp, yp 81 | k-- 82 | } 83 | g.board[y][x].obj = g.board[y][x].base 84 | g.x, g.y = x+dx, y+dy 85 | return 86 | case WALL: 87 | return 88 | case BOX: 89 | if g.board[y+dy][x+dx].obj == BOX { 90 | return 91 | } 92 | } 93 | k++ 94 | } 95 | } 96 | 97 | func (g *Game) move(dir Direction) { 98 | switch dir { 99 | case UP: 100 | g.checkMove(0, -1) 101 | case DOWN: 102 | g.checkMove(0, 1) 103 | case LEFT: 104 | g.checkMove(-1, 0) 105 | case RIGHT: 106 | g.checkMove(1, 0) 107 | } 108 | done := g.checkState() 109 | if done { 110 | g.nextLevel() 111 | } 112 | } 113 | 114 | func (g *Game) checkState() bool { 115 | for _, cells := range g.board { 116 | for _, cell := range cells { 117 | if cell.base == SLOT && cell.obj != BOX { 118 | return false 119 | } 120 | } 121 | } 122 | return true 123 | } 124 | 125 | func (g *Game) nextLevel() { 126 | if g.level < g.db.maxLevel { 127 | g.level = g.level + 1 128 | g.reset() 129 | } 130 | } 131 | 132 | func (g *Game) prevLevel() { 133 | if g.level > 1 { 134 | g.level = g.level - 1 135 | g.reset() 136 | } 137 | } 138 | 139 | func (g *Game) toggleDebug() { 140 | g.debug = !g.debug 141 | } 142 | 143 | // func main() { 144 | // var p = func(g *Game) { 145 | // for i, row := range g.board { 146 | // fmt.Printf("%-2d %q\n", i, row) 147 | // } 148 | // fmt.Println() 149 | // } 150 | // 151 | // g := NewGame() 152 | // p(g) 153 | // g.move(RIGHT) 154 | // p(g) 155 | // } 156 | -------------------------------------------------------------------------------- /levels_db.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | const game_data_file = "sokoban_levels.txt" 11 | 12 | type ldb struct { 13 | maxLevel int 14 | data [][]string 15 | } 16 | 17 | // convert strings to 2-d matrix with bytes 18 | func (db *ldb) getLevel(l int) [][]Cell { 19 | board := make([][]Cell, len(db.data[l-1])) 20 | for i, str := range db.data[l-1] { 21 | board[i] = make([]Cell, len(str)) 22 | for j := 0; j < len(str); j++ { 23 | var c Cell 24 | switch str[j] { 25 | case WALL: 26 | c = Cell{base: WALL, obj: WALL} 27 | case FLOOR: 28 | c = Cell{base: FLOOR, obj: FLOOR} 29 | case GUY: 30 | c = Cell{base: FLOOR, obj: GUY} 31 | case SLOT: 32 | c = Cell{base: SLOT, obj: SLOT} 33 | case BOX: 34 | c = Cell{base: FLOOR, obj: BOX} 35 | case BOXIN: 36 | c = Cell{base: SLOT, obj: BOX} 37 | } 38 | board[i][j] = c 39 | } 40 | } 41 | return board 42 | } 43 | 44 | // Read game database from .txt 45 | func (db *ldb) loadAll() { 46 | f, err := os.Open(game_data_file) 47 | if err != nil { 48 | panic(err) 49 | } 50 | 51 | rd := bufio.NewReader(f) 52 | 53 | var l = 0 54 | var matrix = make([]string, 0) 55 | for { 56 | line, err := rd.ReadString('\n') 57 | if err != nil { 58 | if err == io.EOF { 59 | db.data = append(db.data, matrix) 60 | db.maxLevel = len(db.data) 61 | return 62 | } 63 | panic(err) 64 | } 65 | line = strings.TrimRight(line, "\t\n\f\r") 66 | if len(line) == 0 { 67 | db.data = append(db.data, matrix) 68 | l = l + 1 69 | matrix = make([]string, 0) 70 | } else { 71 | matrix = append(matrix, line) 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /render.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/nsf/termbox-go" 7 | ) 8 | 9 | var title = "-- Sokoban Level %d of %d --" 10 | 11 | const debugConsoleColor = termbox.ColorBlack 12 | const debugTextColor = termbox.ColorWhite 13 | const textColor = termbox.ColorBlack 14 | const backgroundColor = termbox.ColorBlue 15 | const blockSize = 2 16 | const viewStartX = 1 17 | const viewStartY = 1 18 | const titleStartX = viewStartX 19 | const titleStartY = viewStartY 20 | const boardStartX = viewStartX 21 | const boardStartY = titleStartY + 2 22 | const instructionStartY = boardStartY 23 | 24 | var instructionStartX = 0 25 | 26 | var tokenColor = map[byte]termbox.Attribute{ 27 | '@': termbox.ColorWhite, 28 | 'O': termbox.ColorYellow, 29 | '#': termbox.ColorRed, 30 | 'X': termbox.ColorGreen, 31 | ' ': backgroundColor, 32 | } 33 | 34 | const boxinTokenColor = termbox.ColorBlack 35 | 36 | var instructions = []string{ 37 | "Instructions:", 38 | "→ or l :move right", 39 | "← or h :move left", 40 | "↑ or k :move up", 41 | "↓ or j :move down", 42 | " r :reset", 43 | " p :previous level", 44 | " n :next level", 45 | " d :show debug console", 46 | " esc :quit", 47 | "", 48 | "The gola of this game to push all the boxes into the slot without been stuck somewhere.", 49 | } 50 | 51 | var colorInstructions = []struct { 52 | token byte 53 | text string 54 | }{ 55 | {'@', "Player"}, 56 | {'O', "Box"}, 57 | {'#', "Wall"}, 58 | {'X', "Slot"}, 59 | } 60 | 61 | // this function renders debug console and debug messages 62 | func renderDebugConsole(messages []string) { 63 | w, h := termbox.Size() 64 | 65 | for y := 0; y < h; y++ { 66 | for x := w / 2; x < w; x++ { 67 | termbox.SetCell(x, y, ' ', debugConsoleColor, debugConsoleColor) 68 | } 69 | } 70 | 71 | debugTextStartX := w/2 + 2 72 | for y, msg := range messages { 73 | printText(debugTextStartX, y+1, debugTextColor, debugConsoleColor, msg) 74 | } 75 | } 76 | 77 | func debugGameState(g *Game) { 78 | var text []string 79 | for i, cells := range g.board { 80 | var b bytes.Buffer 81 | for _, cell := range cells { 82 | b.WriteByte(cell.obj) 83 | } 84 | text = append(text, fmt.Sprintf("%-2d %s", i, b.String())) 85 | } 86 | text = append(text, " ") 87 | text = append(text, fmt.Sprintf("Where am I => X:%d, Y:%d", g.x, g.y)) 88 | renderDebugConsole(text) 89 | } 90 | 91 | func render(g *Game) { 92 | termbox.Clear(backgroundColor, backgroundColor) 93 | 94 | printText(titleStartX, titleStartY, textColor, backgroundColor, fmt.Sprintf(title, g.level, g.db.maxLevel)) 95 | if g.debug { 96 | debugGameState(g) 97 | } 98 | 99 | var maxWidth = 0 100 | for y, cells := range g.board { 101 | if maxWidth < len(cells) { 102 | maxWidth = len(cells) 103 | } 104 | for x, cel := range cells { 105 | for k := 0; k < blockSize; k++ { 106 | var cellColor = tokenColor[cel.obj] 107 | if cel.obj == BOX && cel.base == SLOT { 108 | cellColor = boxinTokenColor 109 | } 110 | termbox.SetCell(boardStartX+x*blockSize+k, boardStartY+y, ' ', cellColor, cellColor) 111 | } 112 | } 113 | } 114 | 115 | instructionStartX = maxWidth*blockSize + 10 116 | for y, msg := range instructions { 117 | printText(instructionStartX, instructionStartY+y, textColor, backgroundColor, msg) 118 | } 119 | 120 | for i, j := 0, 0; i < len(colorInstructions); i, j = i+1, j+2 { 121 | intr := colorInstructions[i] 122 | for k := 0; k < blockSize; k++ { 123 | termbox.SetCell(instructionStartX+k, instructionStartY+len(instructions)+j+1, ' ', tokenColor[intr.token], tokenColor[intr.token]) 124 | } 125 | printText(instructionStartX+blockSize*2, instructionStartY+len(instructions)+j+1, textColor, backgroundColor, intr.text) 126 | } 127 | 128 | termbox.Flush() 129 | } 130 | 131 | func printText(x, y int, fg, bg termbox.Attribute, msg string) { 132 | for _, c := range msg { 133 | termbox.SetCell(x, y, c, fg, bg) 134 | x++ 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rn2dy/sokoban-go/333f682c14150da0243d0936ec57503aa5dd5b66/screenshot.png -------------------------------------------------------------------------------- /sokoban_levels.txt: -------------------------------------------------------------------------------- 1 | ######### 2 | # @ # 3 | # OXXO # 4 | # # 5 | ######### 6 | 7 | ##### 8 | # # 9 | #O # 10 | ### O## 11 | # O O # 12 | ### # ## # ###### 13 | # # ## ##### XX# 14 | # O O XX# 15 | ##### ### #@## XX# 16 | # ######### 17 | ####### 18 | 19 | ############ 20 | #XX # ### 21 | #XX # O O # 22 | #XX #O#### # 23 | #XX @ ## # 24 | #XX # # O ## 25 | ###### ##O O # 26 | # O O O O # 27 | # # # 28 | ############ 29 | 30 | ######## 31 | # @# 32 | # O#O ## 33 | # O O# 34 | ##O O # 35 | ######### O # ### 36 | #XXXX ## O O # 37 | ##XXX O O # 38 | #XXXX ########## 39 | ######## 40 | 41 | ######## 42 | # XXXX# 43 | ############ XXXX# 44 | # # O O XXXX# 45 | # OOO#O O # XXXX# 46 | # O O # XXXX# 47 | # OO #O O O######## 48 | # O # # 49 | ## ######### 50 | # # ## 51 | # O ## 52 | # OO#OO @# 53 | # # ## 54 | ########### 55 | 56 | ##### 57 | # ##### 58 | # #O## # 59 | # O # 60 | ######### ### # 61 | #XXXX ## O O### 62 | #XXXX O OO ## 63 | #XXXX ##O O @# 64 | ######### O ## 65 | # O O # 66 | ### ## # 67 | # # 68 | ###### 69 | 70 | ###### ### 71 | #XX # ##@## 72 | #XX ### # 73 | #XX OO # 74 | #XX # # O # 75 | #XX### # O # 76 | #### O #O # 77 | # O# O # 78 | # O O # 79 | # ## # 80 | ######### 81 | 82 | ##### 83 | ####### ## 84 | ## # @## OO # 85 | # O # 86 | # O ### # 87 | ### #####O### 88 | # O ### XX# 89 | # O O O XXX# 90 | # ###XXX# 91 | # OO # #XXX# 92 | # ### ##### 93 | #### 94 | 95 | #### 96 | # ########### 97 | # O O O # 98 | # O# O # O # 99 | # O O # # 100 | ### O# # #### # 101 | #@#O O O ## # 102 | # O #O# # # 103 | # O O O O # 104 | ##### ######### 105 | # # 106 | # # 107 | #XXXXXX# 108 | #XXXXXX# 109 | #XXXXXX# 110 | ######## 111 | 112 | ####### 113 | # XXX# 114 | ##### XXX# 115 | # X X# 116 | # ## XXX# 117 | ## ## XXX# 118 | ### ######## 119 | # OOO ## 120 | ##### O O ##### 121 | ## #O O # # 122 | #@ O O O O # 123 | ###### OO O ##### 124 | # # 125 | ######## 126 | 127 | ### ############# 128 | ##@#### # # 129 | # OO OO O O XXX# 130 | # OOO# O #XXX# 131 | # O # OO OO #XXX# 132 | ### # O #XXX# 133 | # # O O O #XXX# 134 | # ###### ###XXX# 135 | ## # # O O #XXX# 136 | # ## # OO O O##XX# 137 | # XX# # O #X# 138 | # XX# # OOO OOO #X# 139 | ##### # # #X# 140 | # ######### #X# 141 | # #X# 142 | ############### 143 | 144 | #### 145 | #### # # 146 | ### @###O # 147 | ## O # 148 | ## O OO## ## 149 | # #O## # 150 | # # O OO # ### 151 | # O # # O ##### 152 | #### # OO # # 153 | #### ## O # 154 | #X ### ######## 155 | #XX XX# #### 156 | #XXX#X# 157 | #XXXXX# 158 | ####### 159 | 160 | ################ 161 | # # 162 | # # ###### # 163 | # # O O O O# # 164 | # # O@O ## ## 165 | # # O O O###XXX# 166 | # # O O ##XXX# 167 | # ###OOO O ##XXX# 168 | # # ## ##XXX# 169 | ##### ## ##XXX# 170 | ##### ### 171 | # # 172 | ####### 173 | 174 | ######### 175 | ## ## ##### 176 | ### # # ### 177 | # O #O # # XXX # 178 | # # O#@O## # #X#X # 179 | # # #O # X X # 180 | # O O # # #X#X # 181 | # ## ##O O X X # 182 | # O # # #O#X#X # 183 | ## O O O OXXX # 184 | #O ###### ## # 185 | # # ########## 186 | #### 187 | 188 | ####### 189 | ####### # 190 | # # O@O # 191 | #OO # ######### 192 | # ###XXXXXX## # 193 | # OXXXXXX## # # 194 | # ###XXXXXX # 195 | ## #### ### #O## 196 | # #O # O # # 197 | # O OOO # O## # 198 | # O O ###OO # # 199 | ##### O # # 200 | ### ### # # 201 | # # # 202 | ######## # 203 | #### 204 | 205 | ######## 206 | # # # 207 | # O # 208 | ### #O #### 209 | # O ##O # 210 | # # @ O # O# 211 | # # O #### 212 | ## ####O## # 213 | # O#XXXXX# # # 214 | # OXX**X O# ### 215 | ## #XXXXX# # 216 | # ### ####### 217 | # OO # # 218 | # # # 219 | ###### # 220 | ##### 221 | 222 | ##### 223 | # ## 224 | # # #### 225 | # O #### # 226 | # OO O O# 227 | ###@ #O ## 228 | # ## O O ## 229 | # O ## ## X# 230 | # #O##O #X# 231 | ### OXX##X# 232 | # #X*XXX# 233 | # OO #XXXXX# 234 | # ######### 235 | # # 236 | #### 237 | 238 | ########## 239 | #XX # # 240 | #XX # 241 | #XX # #### 242 | ####### # ## 243 | # # 244 | # # ## # # 245 | #### ## #### ## 246 | # O ##### # # 247 | # # O O # O # 248 | # @O O # ## 249 | #### ## ####### 250 | # # 251 | ###### 252 | 253 | ########### 254 | # X # # 255 | # #X @ # 256 | ##### ##XX# #### 257 | ## # XX### ### 258 | # O #XXX O # O # 259 | # XX ## ## ## # 260 | ####O##O# O # # # 261 | ## # #O OO # # 262 | # O # # # O## # 263 | # # 264 | # ########### # 265 | #### #### 266 | 267 | ###### 268 | # @#### 269 | ##### O # 270 | # ## #### 271 | # O # ## # 272 | # O # ##### # 273 | ## O O # # 274 | ## O O ### # # 275 | ## # O # # # 276 | ## # #O# # # 277 | ## ### # # ###### 278 | # O #### # #XXXX# 279 | # O O XX#X# 280 | ####O O# O XXXX# 281 | # # ## XXXX# 282 | ################### 283 | 284 | ########## 285 | ##### #### 286 | # # O #@ # 287 | # #######O#### ### 288 | # # ## # #O XX# 289 | # # O # # #X# 290 | # # O # #O XX# 291 | # # ### ## #X# 292 | # ### # # #O XX# 293 | # # # #### #X# 294 | # #O O O #O XX# 295 | # O # O O # #X# 296 | #### O### #O XX# 297 | # OO ###XXXX# 298 | # ## ###### 299 | ######## 300 | 301 | ######### 302 | # # 303 | # #### 304 | ## #### # # 305 | ## #@## # 306 | # OOO O OO# 307 | # # ## O # 308 | # # ## O #### 309 | #### OOO O# # 310 | # ## XXXX# 311 | # # # #XX X# 312 | # # # ##XXX# 313 | ##### O #XXX# 314 | ## ##### 315 | ##### 316 | 317 | ###### #### 318 | # ####### ##### 319 | # O# # O # # 320 | # O O O # O O # 321 | ##O O # @# O # 322 | # O ########### ## 323 | # # #XXXXXXX# O# 324 | # ## # XXXXXX# # 325 | # # OXXXXXXXXO # 326 | # # O #XXXX XX# # 327 | # O O####O#### O# 328 | # O ### O O ## 329 | # O O O O # 330 | ## ###### O ##### # 331 | # # # 332 | ################### 333 | 334 | ####### 335 | # # #### 336 | ##### O#O # ## 337 | #XX # # # # 338 | #XX # O#O # O#### 339 | #X # #O # # 340 | #XX O# # O # 341 | #XX@# #O #O # # 342 | #XX # O# O# # 343 | #XX # #OO#O # ## 344 | #XX # O# # O#O # 345 | #XX # # # # # 346 | ##X #### ##### # 347 | #### #### ##### 348 | 349 | ############### 350 | #XXXXXXXXXX X#### 351 | #XXXXXXXXXXOOX# # 352 | ###########O # ## 353 | # O O O # 354 | ## #### # O # # 355 | # # ## # ## 356 | # O# # ## ### ## 357 | # O #O### ### ## 358 | ### O # # ### ## 359 | ### O ## # # ## 360 | # O # O O O # 361 | # O O#OOO # # 362 | # # O ##### 363 | # @## # # # 364 | ############## 365 | 366 | #### 367 | # ############## 368 | # # XX#XXXXXX# 369 | # # # ##### XXX# 370 | ##O# XXXXXXXX# 371 | # ##O###### #### 372 | # O # ######@ # 373 | ##O # O ###### # 374 | # O #OOO## # 375 | # # #O#O### 376 | # #### #OOOOO # 377 | # # O # # 378 | # # ## ### 379 | # ######O###### O # 380 | # # # # 381 | ########## ##### 382 | 383 | ####### 384 | # # ##### 385 | ## # #XXX### 386 | # O# #XXX # 387 | # O #OO XXX # 388 | # O# #XXX X# 389 | # # O######## 390 | ##O O O # 391 | ## # OO # # 392 | ###### ##OO@# 393 | # ## 394 | ######## 395 | 396 | ################# 397 | #XXX # # ## 398 | ##XXXXX O## # #O # 399 | #XXXXXX# O # # 400 | #XXXXXX# # # # # 401 | ######### O O O # 402 | # #O##O ##O## 403 | ## O # O # 404 | # ## ### # ##O # 405 | # O OO O O # 406 | # O O##O ###### 407 | ####### @ ## 408 | ###### 409 | 410 | ##### 411 | ##### # 412 | ## O O #### 413 | ##### O O O ##X# 414 | # OO ##XX# 415 | # ###### ###XX # 416 | ## # # #XXX # 417 | # O # #XXX # 418 | #@ #O ## ####XXX# 419 | #### O OO ##XX# 420 | ## O O OXXX# 421 | # OO O # X# 422 | # O O #### 423 | ###### # 424 | ##### 425 | 426 | ##### 427 | # ## 428 | # O ######### 429 | ## # # ###### 430 | ## # O#O#@ # # 431 | # # O # O # 432 | # ### ######### ## 433 | # ## XX*XXXXX # ## 434 | ## ## *X*XX*X* # ## 435 | # O########## ##O # 436 | # O O O O # 437 | # # # # # # 438 | ################### 439 | 440 | ########### 441 | # # # 442 | ##### # O O # 443 | # ##### O## # ## 444 | # O ## # ## O # 445 | # O @OO # ##OOO # 446 | ## ### # ## # 447 | ## # ### #####O# 448 | ## # O #XXXX# 449 | # ### ## O #XXXX## 450 | # O O # #XXOX # 451 | # ## O # ##XXXX # 452 | ##### ######XXX## 453 | ##### ##### 454 | 455 | #### 456 | # ######### 457 | ## ## # # 458 | # O# O@O #### 459 | #O O # O O# ## 460 | ## O## #O O # 461 | # # # # OOO # 462 | # O O O## #### 463 | # O O #O# # # 464 | ## ### ###O # 465 | # #XXXX # 466 | ####XXXXXX#### 467 | #XXXX#### 468 | #XXX## 469 | #XXX# 470 | ##### 471 | 472 | #### 473 | ##### # 474 | ## O# 475 | ## O ## ### 476 | #@O O # O # 477 | #### ## O# 478 | #XXXX#O O # 479 | #XXXX# O# 480 | #XXXX OO ## 481 | #XXX # O # 482 | ######O O # 483 | # ### 484 | #O ### 485 | # # 486 | #### 487 | 488 | ########### 489 | # ## # 490 | # O O # 491 | #### ## OO # 492 | # O # # 493 | # OOO # #### 494 | # # # O ## 495 | # # # O # 496 | # O# O# # 497 | # XX# #### 498 | ####XX O #@# 499 | #XXXXX# O# # 500 | ##XXXX# O # 501 | ##XX## # 502 | ########## 503 | 504 | ######### 505 | #XXXX ## 506 | #X#X# O ## 507 | ##XXXX# # @## 508 | # XXXX# # ## 509 | # #O ##O # 510 | ## ### O # 511 | #O O O O# # 512 | # # O O ## # 513 | # ### ## # 514 | # ## ## ## 515 | # O # O # 516 | ###O O ### 517 | # ##### 518 | #### 519 | 520 | ############ ###### 521 | # # # ###XXXX# 522 | # OO# @ XXXXX# 523 | # # ### # XXXX# 524 | ## ## ### # XXXX# 525 | # O O # # #### 526 | # O O## # # 527 | #### # #### # ## # 528 | # # #O ## # # 529 | # O O # ## # ## 530 | # # O O # # # 531 | # O ## ## # ##### 532 | # OO OO # 533 | ## ## ### O # 534 | # # # # 535 | ###### ###### 536 | 537 | ##### 538 | ##### ###### # 539 | # #### O O O # 540 | # O ## ## ## ## 541 | # O O O O # 542 | ### O ## ## ## 543 | # ##### #####OO # 544 | ##O##### @## # 545 | # O ###O### O ## 546 | # O # ### ### 547 | # OO O # OO # 548 | # # ## # 549 | #######XX X### 550 | #XXXXXXXXX# 551 | #XXXXXXXXX# 552 | ########### 553 | 554 | ########### 555 | #XXXXXX ######### 556 | #XXXXXX # ## # 557 | #XX### O O # 558 | #XXX O O # ## # 559 | #XXX#O##### # # 560 | ### # #O #O # 561 | # OO O O O## # 562 | # O #O#O ##O # 563 | ### ## # ## # 564 | # O O ## ###### 565 | # O O # 566 | ## # # # 567 | #####@##### 568 | ### 569 | 570 | #### 571 | ####### @# 572 | # O # 573 | # O## O# 574 | ##O#XXX# # 575 | # OXXX # 576 | # #X X# ## 577 | # # #O # 578 | #O O # 579 | # ####### 580 | #### 581 | 582 | ###### 583 | #############XXXX# 584 | ## ## ##XXXX# 585 | # OO## O @##XXXX# 586 | # OO O# XXXX# 587 | # O ## OO # # XXX# 588 | # O ## O # XXXX# 589 | ## ##### ### ##X### 590 | ## O O ## X # 591 | # O### # ##### ### 592 | # O # # 593 | # O #O O O### # 594 | # OOO# O # #### 595 | # # OO # 596 | ###### ### 597 | ##### 598 | 599 | ############ 600 | # ## 601 | # # #OO O # 602 | #O #O# ## @# 603 | ## ## # O # ## 604 | # O #O # # 605 | # # O # # 606 | ## O O ## # 607 | # # ## O # 608 | # ## OO# # 609 | ######OO # # 610 | #XXXX# ######## 611 | #X#XXX ## 612 | #XXXX # 613 | #XXXX # 614 | ######### 615 | 616 | ##### 617 | ## ## 618 | ## # 619 | ## OO # 620 | ## OO O # 621 | # O O # 622 | #### # OO ##### 623 | # ######## ## # 624 | #X OOO@# 625 | #X# ####### ## ## 626 | #X# #######X #O O## 627 | #XXXXXXXXXXX # # 628 | ############## O # 629 | ## ## 630 | #### 631 | 632 | ######## 633 | #### ###### 634 | # ## O O @# 635 | # ## ##O#O O O## 636 | ### XXXXXX# OO ## 637 | # XXXXXX# # # 638 | # # XXXXXX#O O # 639 | # #OXXXXXX OO# O # 640 | # ### ###O O ## 641 | ### O O O O # 642 | # O O O O # 643 | ###### ###### 644 | ##### 645 | 646 | ####### 647 | ##### # #### 648 | # # O # 649 | #### #OO ## ## # 650 | ## # # ## ### 651 | # ### O#O O O # 652 | #XXX # ## # # 653 | #XXX# @ # ### ## 654 | #XXX# ### O O # 655 | ######## ## # # 656 | ######### 657 | 658 | ##### 659 | # # 660 | # # ####### 661 | # O@###### 662 | # O ##O ### # 663 | # #### O O # 664 | # ##### # #O #### 665 | ## #### ##O # 666 | # O# O # ## ## # 667 | # # #XXX# # 668 | ###### ### XXX # 669 | #### # #XXX# # 670 | # ### # # 671 | # # 672 | ######### 673 | 674 | ##### #### 675 | #XXX# # #### 676 | #XXX### O # 677 | #XXXX## O O### 678 | ##XXXX## O # 679 | ###XXX ## O O # 680 | # ## # O # 681 | # ## # ### #### 682 | # O # #O O # 683 | # O @ O O # 684 | # # O OO O ### 685 | # ###### ### 686 | # ## #### 687 | ### 688 | 689 | ########## 690 | # #### 691 | # ###### # ## 692 | # # O O O O # 693 | # #O # 694 | ###O OO# ### 695 | # ## # O## 696 | ##O# O @# 697 | # O O ### 698 | # # O # 699 | # ## # # 700 | ## ##### # 701 | # # 702 | #XXXXXXX### 703 | #XXXXXXX# 704 | ######### 705 | 706 | #### 707 | ######### ## 708 | ## O O ##### 709 | # ## ## ##XXX# 710 | # #OO O OO#O##XXX# 711 | # # @ # XXX# 712 | # O# ###OO XXX# 713 | # O OO O ##XXXX# 714 | ###O ####### 715 | # ####### 716 | #### 717 | 718 | ######### 719 | #*X*#*X*# 720 | #X*X*X*X# 721 | #*X*X*X*# 722 | #X*X*X*X# 723 | #*X*X*X*# 724 | ### ### 725 | # # 726 | ###### ###### 727 | # # 728 | # O O O O O # 729 | ## O O O O ## 730 | #O O O O O# 731 | # O@O # 732 | # ##### # 733 | #### #### 734 | 735 | #### 736 | # ## 737 | # ## 738 | # OO ## 739 | ###O O ## 740 | #### O # 741 | ### # ##### # 742 | # # #XXXXO # 743 | # # O XXXX# # 744 | # O # #X*XX# # 745 | ### #### ### # 746 | #### @O ##O## 747 | ### O # 748 | # ## # 749 | ######### 750 | 751 | ############ 752 | ##XX # # 753 | ##XX* O O # 754 | ##XX*X# # # O## 755 | #XX*X# # # O # 756 | ####XXX# # # # 757 | # ## # # 758 | # @O O ### # ## 759 | # O O # # # 760 | ###OO # # # # # 761 | # O # # ##### 762 | # O# ##### # 763 | #O # # # # 764 | # ### ## # 765 | # # # ## 766 | #### ###### 767 | --------------------------------------------------------------------------------