From 4b7553cd73d038f46306a2f6da7a55c5c00a4da3 Mon Sep 17 00:00:00 2001
From: faiface <faiface@ksp.sk>
Date: Tue, 30 May 2017 13:30:09 +0200
Subject: [PATCH] add maze generator community example

---
 examples/community/maze/LICENSE           |  21 ++
 examples/community/maze/README.md         |  19 ++
 examples/community/maze/maze-generator.go | 317 ++++++++++++++++++++++
 examples/community/maze/screenshot.png    | Bin 0 -> 14155 bytes
 examples/community/maze/stack/stack.go    |  86 ++++++
 5 files changed, 443 insertions(+)
 create mode 100644 examples/community/maze/LICENSE
 create mode 100644 examples/community/maze/README.md
 create mode 100644 examples/community/maze/maze-generator.go
 create mode 100644 examples/community/maze/screenshot.png
 create mode 100644 examples/community/maze/stack/stack.go

diff --git a/examples/community/maze/LICENSE b/examples/community/maze/LICENSE
new file mode 100644
index 0000000..1326e36
--- /dev/null
+++ b/examples/community/maze/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 stephen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/examples/community/maze/README.md b/examples/community/maze/README.md
new file mode 100644
index 0000000..27b344a
--- /dev/null
+++ b/examples/community/maze/README.md
@@ -0,0 +1,19 @@
+# Maze generator in Go
+
+Created by [Stephen Chavez](https://github.com/redragonx)
+
+This uses the game engine: Pixel. Install it here: https://github.com/faiface/pixel
+
+I made this to improve my understanding of Go and some game concepts with some basic maze generating algorithms.
+
+Controls: Press 'R' to restart the maze.
+
+Optional command-line arguments: `go run ./maze-generator.go`
+  - `-w` sets the maze's width in pixels.
+  -	`-h` sets the maze's height in pixels.
+  -	`-c` sets the maze cell's size in pixels.
+
+Code based on the Recursive backtracker algorithm. 
+- https://en.wikipedia.org/wiki/Maze_generation_algorithm#Recursive_backtracker
+
+![Screenshot](screenshot.png)
\ No newline at end of file
diff --git a/examples/community/maze/maze-generator.go b/examples/community/maze/maze-generator.go
new file mode 100644
index 0000000..a8b09f8
--- /dev/null
+++ b/examples/community/maze/maze-generator.go
@@ -0,0 +1,317 @@
+package main
+
+// Code based on the Recursive backtracker algorithm.
+// https://en.wikipedia.org/wiki/Maze_generation_algorithm#Recursive_backtracker
+// See https://youtu.be/HyK_Q5rrcr4 as an example
+// YouTube example ported to Go for the Pixel library.
+
+// Created by Stephen Chavez
+
+import (
+	"crypto/rand"
+	"errors"
+	"flag"
+	"fmt"
+	"math/big"
+	"time"
+
+	"github.com/faiface/pixel"
+	"github.com/faiface/pixel/examples/community/maze/stack"
+	"github.com/faiface/pixel/imdraw"
+	"github.com/faiface/pixel/pixelgl"
+
+	"github.com/pkg/profile"
+	"golang.org/x/image/colornames"
+)
+
+var visitedColor = pixel.RGB(0.5, 0, 1).Mul(pixel.Alpha(0.35))
+var hightlightColor = pixel.RGB(0.3, 0, 0).Mul(pixel.Alpha(0.45))
+var debug = false
+
+type cell struct {
+	walls [4]bool // Wall order: top, right, bottom, left
+
+	row     int
+	col     int
+	visited bool
+}
+
+func (c *cell) Draw(imd *imdraw.IMDraw, wallSize int) {
+	drawCol := c.col * wallSize // x
+	drawRow := c.row * wallSize // y
+
+	imd.Color = colornames.White
+	if c.walls[0] {
+		// top line
+		imd.Push(pixel.V(float64(drawCol), float64(drawRow)), pixel.V(float64(drawCol+wallSize), float64(drawRow)))
+		imd.Line(3)
+	}
+	if c.walls[1] {
+		// right Line
+		imd.Push(pixel.V(float64(drawCol+wallSize), float64(drawRow)), pixel.V(float64(drawCol+wallSize), float64(drawRow+wallSize)))
+		imd.Line(3)
+	}
+	if c.walls[2] {
+		// bottom line
+		imd.Push(pixel.V(float64(drawCol+wallSize), float64(drawRow+wallSize)), pixel.V(float64(drawCol), float64(drawRow+wallSize)))
+		imd.Line(3)
+	}
+	if c.walls[3] {
+		// left line
+		imd.Push(pixel.V(float64(drawCol), float64(drawRow+wallSize)), pixel.V(float64(drawCol), float64(drawRow)))
+		imd.Line(3)
+	}
+	imd.EndShape = imdraw.SharpEndShape
+
+	if c.visited {
+		imd.Color = visitedColor
+		imd.Push(pixel.V(float64(drawCol), (float64(drawRow))), pixel.V(float64(drawCol+wallSize), float64(drawRow+wallSize)))
+		imd.Rectangle(0)
+	}
+}
+
+func (c *cell) GetNeighbors(grid []*cell, cols int, rows int) ([]*cell, error) {
+	neighbors := []*cell{}
+	j := c.row
+	i := c.col
+
+	top, _ := getCellAt(i, j-1, cols, rows, grid)
+	right, _ := getCellAt(i+1, j, cols, rows, grid)
+	bottom, _ := getCellAt(i, j+1, cols, rows, grid)
+	left, _ := getCellAt(i-1, j, cols, rows, grid)
+
+	if top != nil && !top.visited {
+		neighbors = append(neighbors, top)
+	}
+	if right != nil && !right.visited {
+		neighbors = append(neighbors, right)
+	}
+	if bottom != nil && !bottom.visited {
+		neighbors = append(neighbors, bottom)
+	}
+	if left != nil && !left.visited {
+		neighbors = append(neighbors, left)
+	}
+
+	if len(neighbors) == 0 {
+		return nil, errors.New("We checked all cells...")
+	}
+	return neighbors, nil
+}
+
+func (c *cell) GetRandomNeighbor(grid []*cell, cols int, rows int) (*cell, error) {
+	neighbors, err := c.GetNeighbors(grid, cols, rows)
+	if neighbors == nil {
+		return nil, err
+	}
+	nBig, err := rand.Int(rand.Reader, big.NewInt(int64(len(neighbors))))
+	if err != nil {
+		panic(err)
+	}
+	randomIndex := nBig.Int64()
+	return neighbors[randomIndex], nil
+}
+
+func (c *cell) hightlight(imd *imdraw.IMDraw, wallSize int) {
+	x := c.col * wallSize
+	y := c.row * wallSize
+
+	imd.Color = hightlightColor
+	imd.Push(pixel.V(float64(x), float64(y)), pixel.V(float64(x+wallSize), float64(y+wallSize)))
+	imd.Rectangle(0)
+}
+
+func newCell(col int, row int) *cell {
+	newCell := new(cell)
+	newCell.row = row
+	newCell.col = col
+
+	for i := range newCell.walls {
+		newCell.walls[i] = true
+	}
+	return newCell
+}
+
+// Creates the inital maze slice for use.
+func initGrid(cols, rows int) []*cell {
+	grid := []*cell{}
+	for j := 0; j < rows; j++ {
+		for i := 0; i < cols; i++ {
+			newCell := newCell(i, j)
+			grid = append(grid, newCell)
+		}
+	}
+	return grid
+}
+
+func setupMaze(cols, rows int) ([]*cell, *stack.Stack, *cell) {
+	// Make an empty grid
+	grid := initGrid(cols, rows)
+	backTrackStack := stack.NewStack(len(grid))
+	currentCell := grid[0]
+
+	return grid, backTrackStack, currentCell
+}
+
+func cellIndex(i, j, cols, rows int) int {
+	if i < 0 || j < 0 || i > cols-1 || j > rows-1 {
+		return -1
+	}
+	return i + j*cols
+}
+
+func getCellAt(i int, j int, cols int, rows int, grid []*cell) (*cell, error) {
+	possibleIndex := cellIndex(i, j, cols, rows)
+
+	if possibleIndex == -1 {
+		return nil, fmt.Errorf("cellIndex: CellIndex is a negative number %d", possibleIndex)
+	}
+	return grid[possibleIndex], nil
+}
+
+func removeWalls(a *cell, b *cell) {
+	x := a.col - b.col
+
+	if x == 1 {
+		a.walls[3] = false
+		b.walls[1] = false
+	} else if x == -1 {
+		a.walls[1] = false
+		b.walls[3] = false
+	}
+
+	y := a.row - b.row
+
+	if y == 1 {
+		a.walls[0] = false
+		b.walls[2] = false
+	} else if y == -1 {
+		a.walls[2] = false
+		b.walls[0] = false
+	}
+}
+
+func run() {
+	// unsiged integers, because easier parsing error checks.
+	// We must convert these to intergers, as done below...
+	uScreenWidth, uScreenHeight, uWallSize := parseArgs()
+
+	var (
+		// In pixels
+		// Defualt is 800x800x40 = 20x20 wallgrid
+		screenWidth  = int(uScreenWidth)
+		screenHeight = int(uScreenHeight)
+		wallSize     = int(uWallSize)
+
+		frames = 0
+		second = time.Tick(time.Second)
+
+		grid           = []*cell{}
+		cols           = screenWidth / wallSize
+		rows           = screenHeight / wallSize
+		currentCell    = new(cell)
+		backTrackStack = stack.NewStack(1)
+	)
+
+	// Set game FPS manually
+	fps := time.Tick(time.Second / 60)
+
+	cfg := pixelgl.WindowConfig{
+		Title:  "Pixel Rocks! - Maze example",
+		Bounds: pixel.R(0, 0, float64(screenHeight), float64(screenWidth)),
+	}
+
+	win, err := pixelgl.NewWindow(cfg)
+	if err != nil {
+		panic(err)
+	}
+
+	grid, backTrackStack, currentCell = setupMaze(cols, rows)
+
+	gridIMDraw := imdraw.New(nil)
+
+	for !win.Closed() {
+		if win.JustReleased(pixelgl.KeyR) {
+			fmt.Println("R pressed")
+			grid, backTrackStack, currentCell = setupMaze(cols, rows)
+		}
+
+		win.Clear(colornames.Gray)
+		gridIMDraw.Clear()
+
+		for i := range grid {
+			grid[i].Draw(gridIMDraw, wallSize)
+		}
+
+		// step 1
+		// Make the initial cell the current cell and mark it as visited
+		currentCell.visited = true
+		currentCell.hightlight(gridIMDraw, wallSize)
+
+		// step 2.1
+		// If the current cell has any neighbours which have not been visited
+		// Choose a random unvisited cell
+		nextCell, _ := currentCell.GetRandomNeighbor(grid, cols, rows)
+		if nextCell != nil && !nextCell.visited {
+			// step 2.2
+			// Push the current cell to the stack
+			backTrackStack.Push(currentCell)
+
+			// step 2.3
+			// Remove the wall between the current cell and the chosen cell
+
+			removeWalls(currentCell, nextCell)
+
+			// step 2.4
+			// Make the chosen cell the current cell and mark it as visited
+			nextCell.visited = true
+			currentCell = nextCell
+		} else if backTrackStack.Len() > 0 {
+			currentCell = backTrackStack.Pop().(*cell)
+		}
+
+		gridIMDraw.Draw(win)
+		win.Update()
+		<-fps
+		updateFPSDisplay(win, &cfg, &frames, grid, second)
+	}
+}
+
+// Parses the maze arguments, all of them are optional.
+// Uses uint as implicit error checking :)
+func parseArgs() (uint, uint, uint) {
+	var mazeWidthPtr = flag.Uint("w", 800, "w sets the maze's width in pixels.")
+	var mazeHeightPtr = flag.Uint("h", 800, "h sets the maze's height in pixels.")
+	var wallSizePtr = flag.Uint("c", 40, "c sets the maze cell's size in pixels.")
+
+	flag.Parse()
+
+	// If these aren't default values AND if they're not the same values.
+	// We should warn the user that the maze will look funny.
+	if *mazeWidthPtr != 800 || *mazeHeightPtr != 800 {
+		if *mazeWidthPtr != *mazeHeightPtr {
+			fmt.Printf("WARNING: maze width: %d and maze height: %d don't match. \n", *mazeWidthPtr, *mazeHeightPtr)
+			fmt.Println("Maze will look funny because the maze size is bond to the window size!")
+		}
+	}
+
+	return *mazeWidthPtr, *mazeHeightPtr, *wallSizePtr
+}
+
+func updateFPSDisplay(win *pixelgl.Window, cfg *pixelgl.WindowConfig, frames *int, grid []*cell, second <-chan time.Time) {
+	*frames++
+	select {
+	case <-second:
+		win.SetTitle(fmt.Sprintf("%s | FPS: %d with %d Cells", cfg.Title, *frames, len(grid)))
+		*frames = 0
+	default:
+	}
+
+}
+
+func main() {
+	if debug {
+		defer profile.Start().Stop()
+	}
+	pixelgl.Run(run)
+}
diff --git a/examples/community/maze/screenshot.png b/examples/community/maze/screenshot.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce5bfd2f89336e571b304e1dc224af9f2e231013
GIT binary patch
literal 14155
zcmdsecT|&kzIPN?#<5^@q)2mNlu?=p3W^X%DI<uCqJXq0sGtY|DS?C}I?f0PB<?7n
zQlc=3N(n^>HBl+iL5TEDGyy`7fskJAFU-#F-Fxr*{<G(vduBYx<3mXDJo%PSdt&nU
zC!IHbx&2EB1hVn?cMhi^5S1O^uj3bM!6UbhKe!40Src{i_?a)jpM)<iCV`)|q8+`X
zJrI|pkr%FCf&_;n!Y-Lc1zo>%DLg6!5zSGd+Cv~aAjciPJrkEbH;POcjcF8ekzMyY
zGb(<p5PZKkPb2dF*<a7Bda~}SLpAEus&gpW=qIk<L>=@iowvL_jwkkXuku>6O7(4s
z;g0VwJ8BtJeo!A;k{EtnZRK|Sq{N>1lFnvzI(P4~izjjpG5QZO?j3L>JWU$7I9oy<
zD=8|Pt@OQC-m=!v8LtBc*L&=9m#cYujy*=4%IEWEhzcot)`0ms>Dhiq_k?RvE!(^j
z!Z?kBvEYS~S_{Nj!vkwXz%f-&fZL?W$;puLa6Ht|sre_30AXd*Cfm7gXe2u+X@Gaf
zxYPziYUB^G3o--l#4k-cmUxEp8X%BYdp(PK#Q_5Y;q~*?BMD3G)|By|U9tTym-~G6
z55~;&RKl^MiKk9#>mU%5EGN9qxnb~3d`R7V_3%?fRj+3jx>p4P@%DN2=n-=a&B+<#
z@w(mV;a9Gld(x@03Ib`X&V*y*W4qUzH|z!18*fHk4S{6502k|1DFh#Q-}@|U((~HR
zW8k<=Z58<CFVGzj$dT<UhgYufAV&yM9n>8i2Nge1U<578U42^Q*tyF7fXENOs059_
z{Tr8=V&0kv<0r?p#Uk$9bGM<Ca;$?pvLBT7+n3L4k8NfsIv&cf?I*Vil9Zp^p*aa^
z$czas)gm!aMK-*Tw^$p&(Q}-_@gctu*3wDS(a`1CeuI|@X}1p_<H&}i1j(mlB!%C{
zC~Fxv!$v;t4&KCz8s{=6>_Bc3Nlc1n&LuW(vX{OUwiyzdW4sg<3_6{<v4e9%CP>-e
z?;TVwIYEL4U}B=>yso&ZPl-VbZRWI^NEWk1v6x{q8RSg+(zv0T=%8TNbMW1CY3vAw
zZfAk&nTlgo4>wb>!e;{)S!wy6MJ+A<9!w1{|7`?2eq6&uI|A>nBkS}=jK-+eI;i(C
zu!;c|f#%_Ew@o{|>_V!Z-5!G(xIR(A%w)2u4SPTX2g&y8Nb2qDRQCIaTNUU}<2BO+
zT&hGrZiiE<CUllW4w!4C>6I%el2#Yv5}S|NBnr_r(00xB_`j_AvU2{waI=~1{8zIj
z_2GvPv&<t~krZu)l#etp2<$5!<dU5g>T34Vh@r#zPHA`6)h}!rrcSg&-Jt#cxMMm|
zZ;~y7TH#%n<Ek>CcJ!Q7O^d+fkuee$6DFPSbk-2o>j&7xCyyLho*iAwR#f$>`|>c@
z$CzCPOe24fJk+lmtXwoFd1D_O=HtuIjGBC(Jd2Rdum0P<yS58;#`uGuewmVY69bpr
ztW&@MTZaqhIf0Y!A3~wrTl)T*g~Z6qhq=v+pvL9CG&mgTuY+X1<MJ%&Js5>#m}9>1
zXp6Iamd5OWG0qC+ogJjpcv}`I+tnvj_=s%#oD948&WQCP-+l`_8~3er89`{(CJ!Ds
zFnaM`+&BrBW=TbQ_PoK}+0dp!x^d*fCecULz|5T3J4s12Vw}ODO$SY|k-cXlnUS>C
znIe*UrhJ^~9Y2xYJ*h?J4{>3=^ktdoJqK%!jqW6{>a@d?)M65LgyBWYx1C*GWr<~H
zJ<sjT#mPlAQ${(FZ^~&74$VYPt&u!#wxoRMYEetKJIr4ocx+D^TZ>+%h%G+YclY#s
zl^GCu2^8937d+zIV)7V_Sc_wFaD=B%`}y~#=wO0_`ijp*&UjI_AkedXP^xBzLNBy&
zA;uzVay_V<{F^>4Ycp)b!l1=v-W0{l3MX$X%41<vdt(%td@KxXwNN}==o}RlRs6Ec
zg$n@8(ZPg|omb=vHi)rA>?D%;Nz0P?;TB9&-)`n@Nccj$UNnDhFmkx(dHJG?v1z*G
ziw*6<bz50(C5TjWjGP(-fF&g<DL5B%KrX9HubJmR=MIOGw7TX|U<j-EKA|l>p(WcY
zFSp|Xx*P-@-iqc3#%#FV#N?zT4~jHh6LulcPm;xOaG<~I4@Xmthio_D&sAQgEm;P)
zW>Wae160}LIKirWd5a?RW)JsX<k7}<?Erl!)ZK3RjDthN=Qr!QX$b7f>+HrF5jvRn
zk;)6kz+nJeY}nHE%v*`N(E5d1<XnI3_X(0>j(K$b($y*<Ss^4TPk*9zyz5!-)yTnZ
zUahYa^`Z51<z5Y;3Kb@cc-UUf?eV~@s`xr!Rs86q&{nWgnqGdQ$~gWY+aQnKOR&gy
zrsdYV&q?ZPqu8y-75s;B^qcnr`|SjG_-cAOu<V3WJ>Xo+G5sx|a2n#6!E`SthYcvZ
z4ZLNQlBI{1aPRR{<$iFt?rLRIB3zWCUd~4I)A;yXEU-+>6u8VsC*p%7h%T7ZCdxSG
z&Bf;`C4HmHK7=xCOzpYvwgwWA9PKcD;rVRLn8<?Er?BjG4a(9^_2O7U?TiN6+t=TA
z^6>B|ZtU~1Ba(WV16$qi1mR)j49W86R4t1bALNPbvQT(}gG2FhT|WTLjZ|4!gH5RC
zoMZvn@}M){&>#=fb+Z#pX}=$O^e7q0v!E$I5Q}S+o0OEKWhw6>xAV-3&<l;$t?myr
z6{6aiqY~LVqL}<Z%1SXbVBRyr>{0&42sg=)gey8}dMP-?wVnK|9z98w?pF+3N%k|-
zK#Ze<Uu;Wyq`*$l`io#epX3|WPWgSQrO8H2MQQD>>XE<{;Z!1FCWVlcw9U)UVvJi&
zUY6D7&WAh>Qr&Bl6<%@t#H`#y2hBXr1uRFiq#xc&%Za-_5-aH$h|5pAolw7e&6iea
z{#;yKYwDDAdM6i@p?qR8OMrg<)(9`}_l}txu#vq-&_JzOw!t~NmBDnpdrn+JaSiiJ
z?sJ8J!%ZnF#Utmdyp)xb&h+=UYEXCu-_k8fo+-G)%V$O+**TG)2)Hd!s4t@Gta1nl
zF8i_jCJM`YGklvRL%Uj<EH5`((1@Bi3L6yhS?*$sa$Ey@%Y#^Nn>M9k_#=j%zA?Q&
zQD|!B**g7<xS%<UNV3i6Mv1dP7ul@bjt2*qezK9?Rcnz;C`OO69tD9HXNKGWdqHiq
z3&`GY{L~M1MLO{}XXX$?x@)YSK>=j-j6%|~2y{r*wLP$D0711830XnQy`BUFj#FIo
ziA+s#J23V&{*biHX6ADf(;v;*289}zybNrRmDoCzcxv~cyhr-gUUoPW^s3%!p=FHy
zRD8Nuz(M{48w)+mT)D87+>x{Z8wH#6G6>Av5Es|)w-rXc`r0+ptbQ@r;`&9`6DW?n
zvuEThcEIObHOya$8&Uj%D~~ak8Az&e0BU-|X8dhFvkea0zaU;3T`wKZ1S^6rbSI#n
zx)TmovQ46g)$DoSJ2<5J#GlcLk|vi+>^XS+F*SmsG2;6B99wHW(Jb81Ah6B!07qh7
zgxA#$B{g~;77qli9~~>LpVNUt$)jUiXK)Sk)fD<DN-}Rp%sOOhV6dxUv1%zR`V~4z
zf{S-`aCoU3cO+Z*j>UW>Sm+ms3W@Q%a<P*S2ZEU47J0P?&4ix_fio``u$jY^bbAfu
z7;D<brrXRUHE9S(m<#>qIf7eid=na%6Nk1^`~0g%<8bE@+NSdn6mG9MZS5>cLolj1
zq8!!8xn!f3#_mE|-E8sJLhPj}T5RU|Z!g#{5sCgE=}Ma3inpz1ko?1X&$1p6^mPjp
zrj1Y8P28_p5Nii=>;y?<PT8>$`xu`SIeohGs(PFCB~#7-r5t9ExSHL$@%+L5gy&5v
zYWAA8^VO2oI6()p!EV>df&KCXfgLw73xQX@LH22xpVj{TExu$$rp=^qD@zq7J*5*l
z^;hofY-x%$Lh#gv^%7wc(Pa_b0dwbtQzkNOX0xLL^EX;2(X$gygV!$S6wgtHf3#tK
zIHq26=4pXoOEGr!`0GTyJFaHbz?)Rt#Sh1V<_6BH$Lo{P9--G7dowx@P84!8o%NEF
zo`w*k&-;mA3_q9BV8lxSIN37u0n0u0dH2(n4qH4Se-q5az(76^lNWK&cx^I2u)nFu
z{m#>qvwELrRejhh?Lxlp*mfusF>*OexVW1LK!T=+K8(e6yK!6SUO}w119!3zwL9(O
zy9${L^_+9(pO%nE_MMard4Yj>V^-e`L@HO<((6UDN6#O8HkgQ7EqrxLtyKx&!unZU
zObmmTG8}CGnQ|+zeScpP2dkK?;Vl|7yV^)eIY=FSqKz0H1nQBh1gBl_4^aobaEKdn
z$vp+Y%C^#meSO}TWu;<l)kThOV;r27SOhjF#Z9IQY*Y32qi*7?FY_M6JtGQ2=j)bE
za!U|6qwuUl`}t#0gqn#y-yq)9Dehuh6f0vmJY^J_wV6C>zuyUeEg4CX>8_Val$Stu
z&r&CkpnT^ly7A>rd^^&wyaf*u<7$zRKV1j_{<ikD&2Za!UJa|Xp4mR!c;(6PA<#wU
z!(d|CJ%VEV=xlJLAhb*%DW~a!0`B*OW1i645s|~4OuWB}uI-K2=g=ip%+ziQl%|6_
zI)YTFK%wOwHIdwQ532fXX=TS<gcs0O)uWfQl&Uu{-6AM3rQMp~A3JvCmFgUl(}?sF
zEy0^(`Wc&xiYr`n1YCl%L$eG_Cls0kYyA2U4)t!eViG~H-<}m#@h<dHaL=<-+-lHT
z%gfT83-t=QO-R?xNGXt4fZ9eBN9eCZz?RLS@8a5Ghg*Oq)>LlRh^>qQoK;w+zq%-u
z4a-ICvG<iFywCDVY2*(WIyEl7XxfArwf78&pLmeKb!S>zk7!CL5`XT^TSGy>#!1=6
zJ7f*2<4VFxv8`7Z)(gnwjhZ{$+}yZwK3~XDhytZ^8q`!fBvF&q7l3|3s-IC{w`KZa
z<4+K=@P^OK6rj~cmgoYEoIoDiuEg<c4V_M0`7Kl_4nBg4TRzSR6^R{7miqUxqz6VK
zZV6>>)-Avr=xQTgo+_q}p;WLK2YThGp;Ky-v%bGjYvJ`+fBy@lb~Zz5lnzD_ApZ&g
ztOLLX2-<&zLuF~yv@4^yJNmiElf|`@kIKa}MW%}qg8!ux4i0k-EIjuoI_3ay8d`#r
zM7>0eFh5e14x6n%gP2@zsUbwt=?V6pzG0udBz?Zf(SfUNreI<VTN`{#!yD)Magt58
z%_-&-P4ZZ*@B5-&CjW{DD-1FES0}&<M~c<@)`AIxLh1IBxue8-q?lJTzStI2>1LfK
zSRiwb65-3~UVAiRsOkcd*R1TFH-WOnAmuctJXA^HafZH5W@U<0orC*4{hm-{$H62`
z;*QOS0tM28Jl_4QaY>J0LpbNShvcSe^N;?>a~~PkYUFE4B0ib{kq!hAXcGJA@BE@h
z5zHw9_G{08D+QF<U*S3GZf@<$U84{~EI%x>8)2BMKW+0&O2Kw%g#o}+@&uUOlYVjX
z_(*;{o3_8-e+AQpEoZ%G@xd5#rT1!YZ<kTl$#fCu;k#pBktN~vEe{#8fWsmJu32@H
zkFdRxDv{|kqu@3>mE$iQFDE8x1jJcTxb28#3>G8;kwat@cFoi~8g8Exe(qsd+3csP
z1X*66h<Q1oYV4jczAApbH2yKs!Rq?>Tkk6c+k$@DrcU`f;&@mTsrs4q69KA3@*ZRb
znw%jtX&{(n$KqL1bv2N&EgX4-hf=hePD%(snOld~yD={L`-Yc07F&k<9p(#z%H_t0
zYeXzVR=$L{b#kC*SrOxy$pf)jVdBPi<p2feM1_w;FSRQTSF<gH%cm?aCuW7?gNYck
zU2}DxC%kU;4quU#baII30OcYnJ!$M1=CxO)Qs5<GrarFXD0y#}w!qIEE8A$@+Nm#(
zVAwBST@H5!*r6^k#NPaiow8izM%iB}iJXD9*Fy<E+3=h10ZciWru5?9k!S68?0qkj
zKg|N_#ZO;}UU5ndC}HPnK<yAZDN~~3c8bMGYxB>c01`0>NRH^udeXEcRht~EV>SIM
zPH6+*q+kKrXCbNwjV(t;G_;UoF<uQqW=gr}a#m}fZ@3l|O0DA$VHM_y0nR$8?{xT2
zZm~pcA|c9L_<A*C31+`EEff{N(vr8vrX}iOZJx#;K_n1LLttZKlxO+Y<oV4soxv@^
zIQB3%e~CUTP}tOpGJFjTn1;@JP|jd5F>Wc#M?)wr$b>@$u+K+;j0<Eei=ZiRH4@*4
zf;}~x5NT<)uA5U2yWElR*8^iAte>i%fE_pSD2$<v)J93<ekn<8czk+r`dL(f7J70p
znDr1FuoD1VY?gI%O;tahs91av_bFW>21=GIFe0X=9T@p0b~`l)c)dH36LcKZGMJx$
z{dH$%YeRh10Ork)SVUBR4w}iFTk0Sr<wjs>!?AG_oQ@O_`3zi<3LVD^p^a_W$@h-f
zcILHm(d2qr97`$lK!OzbrnDY7$Bld}2>=T>7sF4c9$x$|Vg6-zATW#=B3d+&DH$u=
zHxT57qg6O6`QqR-Lk@>0WJeA?k8HhC*xITc_LZ8QG=ibls`(}9#;d&tfL{}euP{<U
z^w9hqF1wx}$5AvjkTER*=mn^<`{1%{S#`6BB)XIx%t{*$-%3BcbV8X(u+|fc99&&p
zSBCs>kW+R{M=iHAAMZCOuvG>}6~;JqSlv`!90$ZDv*fWoW7SQ*tX781Y!4B(^T2TQ
zy{WqKE@dp&WY2pt5V+L7f~W8R8IH?k;R!jhI{KEu9jWC-#aom>0flOZ)m+etY5rt^
z?kk=WPw%vt_}MjIc0+~viTXZC8!M?Fh`6;8L@nnq2%Uhis<SMLghL?cA`^7t2c9U&
zC39d=$2$B|TPbQ;-kh)8rI9GQT_lTxV+d;5x#&{!)lO+XwH#U#<@s_N;P7F!8ohw9
zrS=2bf7dzQgnCY+HRbw@b>Mte-<`J7Dbc$Ckcd5FgoW%C>~G2^*s1H=F5D|0dQsGR
zIV*g-si7}(Y`UBEyf7CJ#$!jdB9q*leK0DIb+;)nAS}S|I=5xLo$J`hh>o*P>~{&%
zMR0<e4$50eSCNvQD;CIw5#a|_*yem^!kuGk_BI`d*dxM^7ZO(_>nL*Mw^#~fezK0L
z>0R11C+%=e<k&JhPS)p3dlXs>4K1diK(<jA*-zx)b*mZJSFU|1^I-9UfSx=p7}c-x
zDdB8O>3>1R$W;<`)NyEDC&ep!Z5{krEyEedYvLB#a#Gt!(sO>ernK^2Pv6!y^G2O+
z4%HmYU~SFIC1%TliYyGdq=+958!LOOw1a{XG~!xqi@Gv8=@DF|X%*&dH^Pc7BRRb3
zV-$bo=<%r<C-Kg5alh~_G?8|-sB}9`AALd@#!#eHB;^L?i{T0_PADQ}TENg?2Sj`;
z{WO`SOf@mEt2_@Q9sSu<Z4H?Gc<^D-_`MVvHi%ZbB~efM`oaFt*zA~vTBUVXc*y*e
zb|RX=EfjV-YP8(eF4kwr`mBRe+R*{JB7r;Z5MKT^kyXnkrc}~xVWrX*b7nGC(a1UA
z1p-wi%TJ_x;SJSbCX*dG+QA6|H22MV^VSmbs9rD;86f@WtkL?Vaq(s&Vno8Vs)-CG
z)#bL`2w>q1$X_c@4f5Dru2Rbq%*2t`%ErVP`iz3x=PSvzqt4wimdMS21k!lFL16jp
zr{1+<@y$NbDhY@DR%_Pc^7{vIQ%@3EiTBL!SOhuI6jEdrXJb2Q?DkqTJhM6jYRaYX
ztAY8sHWDKkRb~pQT6Vkag>pg5!$P8ZkP`u)@i)U21<-zA^d-Z!&A@<<qobJ$%*?p=
zYz!&ImeXKdJ#;-~MYP++4g)u~N%G-!<QxsAUQ|-;a~Lf4iGfR*!ai$?{YNwbg?rO0
z1Qx~sWp;B`cIst4f2o@^NfzXyn?WR#3ar{8ENbwQvd=03Gd5>Z>fyH<8KI@+PT9yl
z!Aj0GTUvke#OwlIRS&fnz;3?M@ruV7`v-#hr8umVOVfy-gREtf`vbOWWMlePXZn=X
zwr-DnJs7-Eq<|zRg`ngjwv3z)F}c{0ebC|p2@^PM-SAh6BF8fGSAmW2vmwizaCl3<
zzpSm`x&(5cX8h00oVzO;vVF4KC}(qJaC?AMV|Cg4&w87|ZH_e8G6>{`)#^16$ajWE
z{>!0h6uTpgw*lM{xSluBq5%*2{TN`zKOPT;)i-onDwF!;r&l!k?#PHAd~FTtjI2?2
z94+CP_g?x-8~sC_>70TNjZ^%^l)Z2}6d877;%w)t)n!&25+N4`cOJPh;qU6ue805R
ziN5OY)2jRuP7n=N(N2|}E$Ur+vWx0c&?P7rt)(X{Tfi~5+Y;pZD;Ey|Hxs(P_~Ra0
zxz(3>W_9)UP8}4VoxYZ`@GdPku+zu(O+w6G#?KcNx($8!Tt>Z-FZS3ag=U>guy4LI
zB;?sspAyfa>P+Mfe8k<`{dvtYq|JQ&PBUuKr>YIEmu0K(7uJpkm$_g*-Z(wrJmayr
zXdGYt=?zQ!$U~p)cCVN=-<fBjh%)2lEf2cb+GZwHv5kCVdqjN9lb&T;#bS|X(Z=jw
zaOo}uoS>zRxuTgMikR2IcANUg9T@082kVb^c;!*-g^~jWMMa%ayrQl__0fCUb}e~T
zu@0R}%X=bao`;4F@`6PV5g{&PFdthKmMpzp%44~{TLQC~UfL|n7YJ3$URw2Dt@5dA
zY*0D><E@WBpJs}4aHAPE@Mc)BNhIadON2MJEQGIiKEB6q(b8e3-h;%fpZgIGgG*<J
z>#yC4sgR!Ubg`GOGI29a(>ovOVs~@5Y>O`ays5s+sQmW&HgIy7JU!e-msr$$HQ(9$
zyMoixl7WO<!4&ZWl6~V+{MJJd$X%x!VCSx^`Qi_I=mP?<s;d1c{3^HUnX9}nOcM9x
zgmv20c6w}pXTUKiLK_};EJ$Rk1tzi6aKqb`-G6T#ZCaw6rgwq+^ed^bbLavEb^j2&
zpprdaz>_sC7P(Kik$P?vRU;h>D%%&e^SUziUv>3$O|4Qd>`}cNFbSTplwLZtFjt^g
zO?^^1-%DFm$p|(bz1NnITqn>n3Hjx;fbi)TpOOQpAAh<pt+2ZP3s2Vn=d1m8JIINv
zp`pAlnobu~ny#RAOVOD<ps?P?F^Z7+qzmP;bJ*qG7{&hIG2AuO86dP1p*#ds@wsxA
z^S}auX5?NxUtmUc38o)E3#uPoP|64>Ai<Y<3wa_)M#f|p@g@Lmmt4|Nz~_ey<J(QC
z8*-M{LqvW*I37E=s@PpHp0wM4!nA43E5s!SVe{Ox2wcTqm#D=E%Im)qNG~m6c{+!l
z^<mZjldJw`2KzT3B^}|jArSRW<#z%8SI*pRHUGvZ%*D$qpAFW$5~z?mBj*Q*&yz&b
znG)Se?H5Y?Pi*3QF%zJ6mKhZ6s`KZ1&Hz~G<zIK-Y+&G$O+r>D?PI5SM>u8p?+nNB
z)gb3jJ#xjY-J8d5F6g<J8Bkyj%LpqV1xZvPpRWSIxlzz|T;0Uyxr7!`^JB@y^$n}N
zN)AN)EI+#^okD20j+-nZ{P`i@QK1BPPOi?WnZ>N0K6U6shu5MwZR#9tQu~oeK<G-Z
z!JRpT=KIq!pG^B>A@`pbR2c@k#1r%}YC9T@_7&{Gh|;mEy~W~24hfFneUTE)=r`PC
zGyRJb!Qycj0Q-j$ggH$ElHu5{cVs3=w9&t+@s*`<hH(Mn7Tz3UlCr|Y_fhM9$P{L#
zcDY0J>&f+<hP+VHF<-;H5t)k1dBtPsBn<ZKzp`<+Ez$Z3_<F@38TwaP>uEu!N*T{>
z1%+f+=aZ&#N2YeKjcrO-b3!7K`83T8zz2>b#-}n_`CJ9gM9ZVDZoA!^8++1S@|`vB
zDu?t*=#}}T2d6*FLn-2yu~w)bZl=Q+$4`ZsBmSZv(FMz|Xr3o^4O+rQv*gaL8?M>t
zz~5WP@d-N%o0omVI5WPqBZ^0^xZ6z$R;Q<D$vn|B_hhauwTn#cV&~iMBr&=K{vlSG
z=S^94MwyB&G0BI~RTl^3=R4>0k6HTR=><-2rEMg@6<~eZaf5e7YY{c?|13fYe9Oj!
z`a2_+HSEDiLg$n6*g=B_o|gcedKw+GKknz7%N^%lbY;Q|PG8}Dp|WoU=2tf5wIoq=
z`{~fww)BqbKz30y|8NLsP~6>?Z(>Bt`xqZK6wZ?_DwzW8+n6k$2QkAalD>(O9bM<+
z0(<oUG4)~|pRl|Kqm|JhbG#4JUoOY>06ux<J}ptT`q{NC<k`Zz393VTMH^qWDN{zq
z0!7woXmU~Bb{n*~>5@I-sOlfv8?4wGH`jbsZ}s1pTIG?_C_rx)02>hP9?tv9gyh1I
z``G49iM;o@;1Z0_PD>CZ;FPAIYcEp7_YcE8fK50%+-Cc2Nwj?Fv64g9cX(H9$s{h<
zkKd<A0eSG|5jEG)P2VQZS_cZ}^b0Db=i{!LHGXi-B{i!siqh@#mwkz#zte4H;}gy@
zmofS6Ci$sDZECm8Gtc+FTNN?rIU`=)FueE#UE%u86`f(Y?8S;^eJjnJ06g~Ii#R4~
zSf~3xxyyf(oBvn%_`g};|8$W`v2IfoUF|3scXBTeDbYQ}OK4nCus{A?!K&<u5pB$z
zCp?Li{-!f|^CYi-tOk&-ZXk2=3^V7fDe4l@@4e^(+vLgQYh2LwJMH2}EznmOT*I}c
zY$3yj^)}$z3t+dt7blnuyCi&`Kf8FX$x+z_7U%_De2(_2i@BxvP>j3T(w3aD;3StE
z!p{7jt!suEi%)vyNzqD>`%T2Y#|@^eIA<-hKaFz$>a~sZ`x>P54M830{h>+~<*b<j
zb^`SqsepD!w9RJ8XPPdFfVy!a?A@`XRFb>nDH(nEq}`Y7vEZvf$ImDKUCVmTB{Tvq
z*lCQRBrWzMfKe#si41=Aqo#M{h8P3FufuJ~oKW2W2c2m;wV2S$FMU?6A)K-R8=?>1
zRpxZxjyO-k0^<1e>cAH!B&EVl7ehuQ8_o;H_nBKv=McpA>tyNSHP>(j3)H6LRX$<7
zFV;U)RBZY48EwEHq?TW*tDOiRNTAv)(FWJWAKR#84%C{AywharrwYXoEW-_O1h5<b
zxkVO?5Bq=f*zZ+qT!J4OnVFme>UIf%p48qh;rx6`EH;g#bP3cXYs(r!%|u!cw&>e<
zSGd4wJ30E$OLQ-Bo$e+VIJJPp`WZOD#bTjrnG5cQih4<pj4aTaT6lLUfH|(UIN|({
z7Hs^z1sgK|EQsaSZC2d7-L5(BWvs&rW29VO;RR(HH+s)d*@)N&*fYw^V`Rdbs>J!F
z^k3z+(T%ej=i__N6s2qL`2L5h=y;>O&?ffRPk9qw;)EU`o<c`gx7n^*t@6#XR!=|y
z@$LP%MeFSQ6s)V)3_rMWbz6N#mwVY`=<T+3N1q#cn`i-|?=rujeeySM_}4t+&%@Q-
zFV}}w?e_Oi%gu-18yYe&$m<dPE}%?&LV>SK`ptD*lW4zC20|ib<|F~)NGEGv|5!IV
zVU>5l-^ac|^=_B`M-(6kuSNOm3P98(;HJNEk9tnu{O!6qeIQteLAO?Y|1`r%ZSP3B
z_{)vUT4waeL-Gu_rb`m-P5)D=fp6y|jQzOjU%yZNM~m=QuJ;l6*sr=ge$Py~e+QM-
z!;GITo|lw>yeRZvVxyH5dL_a5K_Ay{Z3Q6x9w33WYKz<N*GN(6by#%zA>usZ-8V{{
zm#zA~8srvFtQZv~Ljn9j*A;i*5-b9lhD_tezS=dRTK#slQr8pS)gYmEB9z_z8J>|#
z1Uj?;ft%Dmyuvde^+2(gK=KY^_CI=3{n82r$$$f(UU6yOLn|ii{BBJ%31WJfxDptE
zY;sb&ZiQL>)AWKmU2;gj>;KvPI+MnO5#HFHbg#P80A^^JfV~~=Qx&Vh0Cy2z?}?{e
zMc)iwOFAuHoVEMg?z7XgHRH>+I`<o-E(|{8{Iiv?=$#M$pVih22l)P<N)7G`{7FVz
zc^l>GirJ+d1^5Alf>8AJtqqT>@<&8U05OlDBvoX>L7v=B#k!8+0P2d<H1&q{AsZZj
zm;5)AHSLWoK76-xfy(#)EfTtIn)iOI@i)icNz5TfaKGgb+yA-*H}GWc+-ov?yW4of
zZzk%O)6aRBYyT3y{h3C2<)%w0Z*xl5kg~F!9z0T2K3lNx4yvrV?Rx`2h<E(4&`TtS
zD7^-23_5UXf!}Yd4|!Dt(0O9*+lW(P$kjC_#uyn0{eKMG44ERjmd#k{<h~A`a<QxB
zoL%I5i7Qkf+w%*0Ty0#e{c=aEuvdOLZFupf*WDNY{f3tE9$9ZNhz_=G{{Lac4F!YJ
z$lA4G&G%pF8dS5|o2>%UCfbkOI=8{(?e5K|?7u#(IUWCf$CVSJae@^Yazj)3-({Lt
zeB{c_0t=;?Nm_x=R3#n-Dr1<<E0*W~=&1+TvWo1<_<3DqBOkN7;J&&bjW%1|_L}0H
zMx*#`3MC28S>L}bh-q3LnxPPU5)|PKRmffP{O!{x=X2ETbszg$%}?&rlXT6<>f>)?
zOe;wNZ5(~bT|w8(DuvdG`q~rq)$U%VFh(CoclmG-<YUqNkcyC@^ZmJDE4NU0;bk3~
zfWepLU}ENqg5bK6nZs@NIAw6~e_xt^qc_(&B$v*io9#v;8Ur@XE(qPMU)IxQMZxf#
i48bTx4YGr?abzeYPkr#iHgF3Ga@_HxL+R1;Kl}&S;TXpN

literal 0
HcmV?d00001

diff --git a/examples/community/maze/stack/stack.go b/examples/community/maze/stack/stack.go
new file mode 100644
index 0000000..50a7a46
--- /dev/null
+++ b/examples/community/maze/stack/stack.go
@@ -0,0 +1,86 @@
+package stack
+
+type Stack struct {
+	top  *Element
+	size int
+	max  int
+}
+
+type Element struct {
+	value interface{}
+	next  *Element
+}
+
+func NewStack(max int) *Stack {
+	return &Stack{max: max}
+}
+
+// Return the stack's length
+func (s *Stack) Len() int {
+	return s.size
+}
+
+// Return the stack's max
+func (s *Stack) Max() int {
+	return s.max
+}
+
+// Push a new element onto the stack
+func (s *Stack) Push(value interface{}) {
+	if s.size+1 > s.max {
+		if last := s.PopLast(); last == nil {
+			panic("Unexpected nil in stack")
+		}
+	}
+	s.top = &Element{value, s.top}
+	s.size++
+}
+
+// Remove the top element from the stack and return it's value
+// If the stack is empty, return nil
+func (s *Stack) Pop() (value interface{}) {
+	if s.size > 0 {
+		value, s.top = s.top.value, s.top.next
+		s.size--
+		return
+	}
+	return nil
+}
+
+func (s *Stack) PopLast() (value interface{}) {
+	if lastElem := s.popLast(s.top); lastElem != nil {
+		return lastElem.value
+	}
+	return nil
+}
+
+//Peek returns a top without removing it from list
+func (s *Stack) Peek() (value interface{}, exists bool) {
+	exists = false
+	if s.size > 0 {
+		value = s.top.value
+		exists = true
+	}
+
+	return
+}
+
+func (s *Stack) popLast(elem *Element) *Element {
+	if elem == nil {
+		return nil
+	}
+	// not last because it has next and a grandchild
+	if elem.next != nil && elem.next.next != nil {
+		return s.popLast(elem.next)
+	}
+
+	// current elem is second from bottom, as next elem has no child
+	if elem.next != nil && elem.next.next == nil {
+		last := elem.next
+		// make current elem bottom of stack by removing its next element
+		elem.next = nil
+		s.size--
+		return last
+	}
+	return nil
+}
-- 
GitLab