├── .ruby-version ├── source ├── layout.haml ├── images │ └── twitter-48x48.png ├── stylesheets │ ├── site.css.scss │ └── layout.css.sass ├── index.html.haml └── javascripts │ └── mazery.js.coffee ├── .gitignore ├── Gemfile ├── README.md ├── config.rb └── Gemfile.lock /.ruby-version: -------------------------------------------------------------------------------- 1 | 1.9.3-p551 2 | -------------------------------------------------------------------------------- /source/layout.haml: -------------------------------------------------------------------------------- 1 | = yield 2 | -------------------------------------------------------------------------------- /source/images/twitter-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevejackson/mazery/HEAD/source/images/twitter-48x48.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | /_site/* 3 | /bin/* 4 | /.sass-cache 5 | /css/.sass-cache/* 6 | /rakefile 7 | /build 8 | *.DS_Store 9 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem 'middleman', '~> 3.3' 4 | gem 'therubyracer' 5 | 6 | gem 'compass-blueprint' 7 | -------------------------------------------------------------------------------- /source/stylesheets/site.css.scss: -------------------------------------------------------------------------------- 1 | @import "compass"; 2 | 3 | $link-color: #0388a6; 4 | $link-hover-color: #009ce0; 5 | $link-focus-color: false; 6 | $link-active-color: false; 7 | $link-visited-color: false; 8 | 9 | $font-color: #2a2a2a; 10 | $font-family: sans-serif; 11 | $base-font-size: 12px; 12 | $base-line-height: 18px; 13 | 14 | $total-cols: 12; 15 | $col-width: 4em; 16 | $gutter-width: 1em; 17 | $side-gutter-width: $gutter-width; 18 | 19 | @include global-reset; 20 | 21 | body { 22 | font-family: $font-family; 23 | color: $font-color; 24 | } 25 | 26 | a { 27 | @include link-colors($link-color, $link-hover-color, $link-focus-color, $link-active-color, $link-visited-color); 28 | } 29 | 30 | #main { 31 | padding: 50px; 32 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | [Mazery](http://mazery.sjackson.net) is a maze generation visualizer project written in CoffeeScript. 4 | 5 | Why? Because maze generation is fun to watch! 6 | 7 | ## Setup 8 | 9 | If you want to fork it and fool around, it's easy. The site runs on [middleman](https://github.com/tdreyno/middleman), 10 | a simple static site generator. 11 | 12 | It runs on ruby 1.9.3. 13 | 14 | gem install bundler 15 | bundle install 16 | 17 | Now you're all set up. When you want to run the server, which autocompiles the coffeescript/sass etc files into /build 18 | as they're edited, run: 19 | 20 | middleman 21 | 22 | That's it! Your server is now running and can be accessed at: 23 | 24 | localhost:4567 25 | 26 | Your generated site is now in /build. 27 | -------------------------------------------------------------------------------- /source/index.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | 3 | %html 4 | %head 5 | /[if lt IE 9] 6 | %script(src="//html5shim.googlecode.com/svn/trunk/html5.js") 7 | %link{ :type => 'text/css', :href => 'stylesheets/layout.css', :rel=>'stylesheet' } 8 | %link{ :type => 'text/css', :href => 'http://fonts.googleapis.com/css?family=Open+Sans:400,300,700', :rel => 'stylesheet' } 9 | %script{:src => '//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js', 10 | :type => 'text/javascript' } 11 | %script{:type => 'text/javascript', 12 | :src => 'javascripts/mazery.js'} 13 | 14 | %title mazery by Steve Jackson 15 | 16 | %body 17 | %header 18 | .primary_layout 19 | %span.bigger 20 | %a{ :href => '/', :alt => "mazery" } mazery 21 | %span.smaller 22 | %a{ :href => 'http://sjackson.net' } random maze generation visualization 23 | 24 | %hr.space 25 | 26 | %content 27 | .primary_layout 28 | %a.button#generate Generate 29 | %label{ :for => 'fps' }Max FPS 30 | %input#fps{ :name => 'fps', :type => 'text', :value => '60', :size => 2, :maxlength => 2 } 31 | %label{ :for => 'cellsize' }Cell Size 32 | %input#cellsize{ :name => 'cellsize', :type => 'text', :value => '35', :size => 2, :maxlength => 2 } 33 | %a.left-buffer{ :href => 'https://github.com/stevejackson/mazery', :alt => "Open source on GitHub" } 34 | Open source on GitHub. 35 | %span.left-buffer 36 | Written by 37 | %a{ :href => 'http://www.sjackson.net' } 38 | Steve Jackson. 39 | %hr.space 40 | %canvas#mazecanvas{ :width => 900, :height => 900 } 41 | Maze Canvas. 42 | -------------------------------------------------------------------------------- /config.rb: -------------------------------------------------------------------------------- 1 | ### 2 | # Compass 3 | ### 4 | 5 | # Susy grids in Compass 6 | # First: gem install compass-susy-plugin 7 | # require 'susy' 8 | 9 | # Change Compass configuration 10 | compass_config do |config| 11 | config.output_style = :compact 12 | config.sass_options = { 13 | :line_comments => false 14 | } 15 | end 16 | 17 | ### 18 | # Haml 19 | ### 20 | 21 | # CodeRay syntax highlighting in Haml 22 | # First: gem install haml-coderay 23 | # require 'haml-coderay' 24 | 25 | # CoffeeScript filters in Haml 26 | # First: gem install coffee-filter 27 | # require 'coffee-filter' 28 | 29 | # Automatic image dimensions on image_tag helper 30 | # activate :automatic_image_sizes 31 | 32 | ### 33 | # Page command 34 | ### 35 | 36 | # Per-page layout changes: 37 | # 38 | # With no layout 39 | # page "/path/to/file.html", :layout => false 40 | # 41 | # With alternative layout 42 | # page "/path/to/file.html", :layout => :otherlayout 43 | # 44 | # A path which all have the same layout 45 | # with_layout :admin do 46 | # page "/admin/*" 47 | # end 48 | 49 | # Proxy (fake) files 50 | # page "/this-page-has-no-template.html", :proxy => "/template-file.html" do 51 | # @which_fake_page = "Rendering a fake page with a variable" 52 | # end 53 | 54 | ### 55 | # Helpers 56 | ### 57 | 58 | # Methods defined in the helpers block are available in templates 59 | # helpers do 60 | # def some_helper 61 | # "Helping" 62 | # end 63 | # end 64 | 65 | # Change the CSS directory 66 | # set :css_dir, "alternative_css_directory" 67 | 68 | # Change the JS directory 69 | # set :js_dir, "alternative_js_directory" 70 | 71 | # Change the images directory 72 | # set :images_dir, "alternative_image_directory" 73 | 74 | # Build-specific configuration 75 | configure :build do 76 | # For example, change the Compass output style for deployment 77 | # activate :minify_css 78 | 79 | # Minify Javascript on build 80 | # activate :minify_javascript 81 | 82 | # Enable cache buster 83 | # activate :cache_buster 84 | 85 | # Use relative URLs 86 | # activate :relative_assets 87 | 88 | # Compress PNGs after build 89 | # First: gem install middleman-smusher 90 | # require "middleman-smusher" 91 | # activate :smusher 92 | 93 | # Or use a different image path 94 | # set :http_path, "/Content/images/" 95 | end 96 | -------------------------------------------------------------------------------- /source/stylesheets/layout.css.sass: -------------------------------------------------------------------------------- 1 | @import blueprint/reset 2 | @import blueprint 3 | @import compass 4 | 5 | // Theme style: http://paletton.com/#uid=73v0x0kd7tIfGBkeVvOeoorepkO 6 | 7 | $background-color: #3D586A 8 | 9 | $text-color: white 10 | $link-color: #FDE087 11 | $button-color: #91BD09 12 | $theme2: #EDEB8C 13 | 14 | $offset-color: #6D6CA5 15 | 16 | \::selection, ::-moz-selection, ::-webkit-selection 17 | background: $theme2 18 | 19 | body 20 | +blueprint-typography(true) 21 | +blueprint 22 | font-family: "Open Sans", Helvetica, Arial, sans-serif 23 | background: $background-color 24 | 25 | .warning 26 | margin-left: 20px 27 | display: inline-block 28 | 29 | body label 30 | margin-left: 20px 31 | color: white 32 | margin-right: 5px 33 | 34 | body input[type="text"] 35 | width: 20px 36 | 37 | p, span, h1, h2, h3 38 | color: $text-color 39 | 40 | header a, header a:visited 41 | color: white 42 | text-decoration: none 43 | 44 | &:hover 45 | color: $link-color 46 | 47 | content a, content a:visited 48 | color: $link-color 49 | text-decoration: none 50 | 51 | &:hover 52 | color: darken($link-color, 10%) 53 | text-decoration: underline 54 | 55 | a.button, a.button:visited 56 | &:hover 57 | text-decoration: none 58 | 59 | .primary_layout 60 | +container 61 | width: 900px 62 | header 63 | +column(24) 64 | content 65 | .controls 66 | +column(24) 67 | .board 68 | +column(24) 69 | 70 | header 71 | span.bigger 72 | font-size: 4em 73 | font-weight: bold 74 | color: #484774 75 | span.smaller 76 | font-size: 2em 77 | margin-left: 20px 78 | 79 | hr.space 80 | +colspacer 81 | 82 | .button, .button:visited 83 | background: $offset-color 84 | display: inline-block 85 | color: white 86 | padding: 10px 15px 11px 87 | text-decoration: none 88 | font-weight: bold 89 | font-size: 2em 90 | line-height: 1em 91 | +border-radius(5px) 92 | +box-shadow(0 3px 3px rgba(0,0,0,1)) 93 | +text-shadow(0 -1px 1px rgba(0,0,0,0.25)) 94 | position: relative 95 | cursor: pointer 96 | border-bottom: 1px solid rgba(0,0,0,0.25) 97 | &:hover 98 | color: white 99 | background: darken($offset-color, 10%) 100 | &:active 101 | top: 1px 102 | 103 | .right 104 | float: right 105 | 106 | .left-buffer 107 | padding-left: 20px 108 | 109 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | activesupport (4.1.9) 5 | i18n (~> 0.6, >= 0.6.9) 6 | json (~> 1.7, >= 1.7.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.1) 9 | tzinfo (~> 1.1) 10 | celluloid (0.16.0) 11 | timers (~> 4.0.0) 12 | chunky_png (1.3.4) 13 | coffee-script (2.3.0) 14 | coffee-script-source 15 | execjs 16 | coffee-script-source (1.9.1) 17 | compass (1.0.3) 18 | chunky_png (~> 1.2) 19 | compass-core (~> 1.0.2) 20 | compass-import-once (~> 1.0.5) 21 | rb-fsevent (>= 0.9.3) 22 | rb-inotify (>= 0.9) 23 | sass (>= 3.3.13, < 3.5) 24 | compass-blueprint (1.0.0) 25 | compass 26 | compass-core (1.0.3) 27 | multi_json (~> 1.0) 28 | sass (>= 3.3.0, < 3.5) 29 | compass-import-once (1.0.5) 30 | sass (>= 3.2, < 3.5) 31 | erubis (2.7.0) 32 | execjs (2.3.0) 33 | ffi (1.9.6) 34 | haml (4.0.6) 35 | tilt 36 | hike (1.2.3) 37 | hitimes (1.2.2) 38 | hooks (0.4.0) 39 | uber (~> 0.0.4) 40 | i18n (0.7.0) 41 | json (1.8.2) 42 | kramdown (1.5.0) 43 | libv8 (3.16.14.7) 44 | listen (2.8.5) 45 | celluloid (>= 0.15.2) 46 | rb-fsevent (>= 0.9.3) 47 | rb-inotify (>= 0.9) 48 | middleman (3.3.9) 49 | coffee-script (~> 2.2) 50 | compass (>= 1.0.0, < 2.0.0) 51 | compass-import-once (= 1.0.5) 52 | execjs (~> 2.0) 53 | haml (>= 4.0.5) 54 | kramdown (~> 1.2) 55 | middleman-core (= 3.3.9) 56 | middleman-sprockets (>= 3.1.2) 57 | sass (>= 3.4.0, < 4.0) 58 | uglifier (~> 2.5) 59 | middleman-core (3.3.9) 60 | activesupport (~> 4.1.0) 61 | bundler (~> 1.1) 62 | erubis 63 | hooks (~> 0.3) 64 | i18n (~> 0.7.0) 65 | listen (>= 2.7.9, < 3.0) 66 | padrino-helpers (~> 0.12.3) 67 | rack (>= 1.4.5, < 2.0) 68 | rack-test (~> 0.6.2) 69 | thor (>= 0.15.2, < 2.0) 70 | tilt (~> 1.4.1, < 2.0) 71 | middleman-sprockets (3.4.1) 72 | middleman-core (>= 3.3) 73 | sprockets (~> 2.12.1) 74 | sprockets-helpers (~> 1.1.0) 75 | sprockets-sass (~> 1.3.0) 76 | minitest (5.5.1) 77 | multi_json (1.10.1) 78 | padrino-helpers (0.12.4) 79 | i18n (~> 0.6, >= 0.6.7) 80 | padrino-support (= 0.12.4) 81 | tilt (~> 1.4.1) 82 | padrino-support (0.12.4) 83 | activesupport (>= 3.1) 84 | rack (1.6.0) 85 | rack-test (0.6.3) 86 | rack (>= 1.0) 87 | rb-fsevent (0.9.4) 88 | rb-inotify (0.9.5) 89 | ffi (>= 0.5.0) 90 | ref (1.0.5) 91 | sass (3.4.12) 92 | sprockets (2.12.3) 93 | hike (~> 1.2) 94 | multi_json (~> 1.0) 95 | rack (~> 1.0) 96 | tilt (~> 1.1, != 1.3.0) 97 | sprockets-helpers (1.1.0) 98 | sprockets (~> 2.0) 99 | sprockets-sass (1.3.1) 100 | sprockets (~> 2.0) 101 | tilt (~> 1.1) 102 | therubyracer (0.12.1) 103 | libv8 (~> 3.16.14.0) 104 | ref 105 | thor (0.19.1) 106 | thread_safe (0.3.4) 107 | tilt (1.4.1) 108 | timers (4.0.1) 109 | hitimes 110 | tzinfo (1.2.2) 111 | thread_safe (~> 0.1) 112 | uber (0.0.13) 113 | uglifier (2.7.0) 114 | execjs (>= 0.3.0) 115 | json (>= 1.8.0) 116 | 117 | PLATFORMS 118 | ruby 119 | 120 | DEPENDENCIES 121 | compass-blueprint 122 | middleman (~> 3.3) 123 | therubyracer 124 | -------------------------------------------------------------------------------- /source/javascripts/mazery.js.coffee: -------------------------------------------------------------------------------- 1 | # steve jackson, 2011 2 | # use at your own peril. 3 | 4 | $(document).ready -> 5 | mazery() 6 | 7 | # 'Generate' button handler 8 | $('#generate').click -> 9 | cellSize = parseInt($('#cellsize').val()) 10 | if(cellSize <= 30) 11 | if(confirm("Low cell size values could slow down and crash your browser. Proceed?")) 12 | mazery() 13 | else 14 | mazery() 15 | 16 | mazery = -> 17 | # grab the context 18 | canvas = $('#mazecanvas').get(0) 19 | canvas.width = canvas.width 20 | canvasWidth = canvas.width 21 | canvasHeight = canvas.height 22 | 23 | # get the inputs from the form 24 | cellSize = parseInt($('#cellsize').val()) 25 | framesPerSecond = parseInt($('#fps').val()) 26 | 27 | # get the real canvas size that we can fit. 28 | for x in [0..canvasWidth] 29 | if x + cellSize * 2 > canvasWidth 30 | mazeWidth = x 31 | break 32 | x += cellSize 33 | for y in [0..canvasHeight] 34 | if y + cellSize * 2 > canvasHeight 35 | mazeHeight = y 36 | break 37 | y += cellSize 38 | 39 | if canvas.getContext 40 | context = canvas.getContext('2d') 41 | @maze = new Maze context, Math.floor(mazeWidth / cellSize), Math.floor(mazeHeight / cellSize), cellSize 42 | 43 | logicLoop = -> 44 | # clear the screen every frame. 45 | context.clearRect(0, 0, canvas.width, canvas.height) 46 | # rendering / logic 47 | @maze.update() 48 | @maze.drawAllCells() 49 | 50 | $('#generate').click -> 51 | clearInterval(mazeInterval) 52 | 53 | # start our loop! 54 | mazeInterval = setInterval(logicLoop, 1000 / framesPerSecond) 55 | 56 | class Maze 57 | constructor: (@context, @width, @height, @cellSize) -> 58 | @cells = new Array(@width) 59 | 60 | for i in [0..@width] 61 | @cells[i] = new Array(@height) 62 | 63 | for i in [0..@width] 64 | for j in [0..@height] 65 | @cells[i][j] = new Cell(i, j) 66 | 67 | @location = @getRandomCell() 68 | @cells[@location[0]][@location[1]].visited = true 69 | @hunting = false 70 | @complete = false 71 | 72 | getRandomCell: -> 73 | x = Math.floor(Math.random() * @width) 74 | y = Math.floor(Math.random() * @height) 75 | [x, y] 76 | 77 | getCellColor: (x, y) -> 78 | if @cells[x][y].visited 79 | return "#3D586A" # filled in background color 80 | else 81 | return "white" # blank background color 82 | 83 | "red" # error 84 | 85 | drawCell: (x, y) -> 86 | # draw order: 87 | # * EITHER "empty" cell color, or "traversed" cell color 88 | # * "recently visited" cell color, fades away 89 | # * "hunted" cell color, fades away 90 | 91 | @cells[x][y].update() 92 | @context.globalAlpha = @cells[x][y].alpha 93 | locX = x * @cellSize 94 | locY = y * @cellSize 95 | 96 | # draw either "empty" cell color, or "traversed" cell color 97 | @context.fillStyle = @getCellColor(x, y) 98 | @context.fillRect(locX, locY, @cellSize, @cellSize) 99 | @context.fill() 100 | 101 | # draw the "recently visited" cell color, fades away 102 | if @cells[x][y].recentlyVisited 103 | @context.fillStyle = "#EDEB8C" 104 | @context.globalAlpha = @cells[x][y].visitedAlpha 105 | @context.fillRect(locX, locY, @cellSize, @cellSize) 106 | @context.fill() 107 | 108 | # draw the "hunted" cell color, fades away 109 | if @cells[x][y].hunted 110 | @context.fillStyle = "#EDEB8C" 111 | @context.globalAlpha = @cells[x][y].huntedAlpha 112 | @context.fillRect(locX, locY, @cellSize, @cellSize) 113 | @context.fill() 114 | 115 | drawAllCellBorders: (x, y) -> 116 | @context.strokeStyle = "#eaeaea" 117 | @context.globalAlpha = 1 118 | 119 | for x in [0..@width] 120 | for y in [0..@height] 121 | @context.beginPath() 122 | if @cells[x][y].north 123 | @context.moveTo(x * @cellSize, y * @cellSize) 124 | @context.lineTo(x * @cellSize + @cellSize, y * @cellSize) 125 | if @cells[x][y].south 126 | @context.moveTo(x * @cellSize, y * @cellSize + @cellSize) 127 | @context.lineTo(x * @cellSize + @cellSize, y * @cellSize + @cellSize) 128 | if @cells[x][y].east 129 | @context.moveTo(x * @cellSize + @cellSize, y * @cellSize) 130 | @context.lineTo(x * @cellSize + @cellSize, y * @cellSize + @cellSize) 131 | if @cells[x][y].west 132 | @context.moveTo(x * @cellSize, y * @cellSize) 133 | @context.lineTo(x * @cellSize, y * @cellSize + @cellSize) 134 | @context.closePath() 135 | @context.stroke() 136 | 137 | drawAllCells: -> 138 | for x in [0..@width] 139 | for y in [0..@height] 140 | @drawCell(x, y) 141 | @drawAllCellBorders() 142 | 143 | update: -> 144 | # maze generation logic 145 | return true if @complete 146 | 147 | if @hunting 148 | # when we're hunting, we're sweeping down row by row to find a cell that is 149 | # unvisited, and adjacent to a visited cell. 150 | hunt = @huntInRow(@location[1]) 151 | if hunt 152 | @hunting = false 153 | @location = hunt 154 | neighbor = @getNeighbor(@location[0], @location[1], true) 155 | unless not neighbor 156 | @cells[neighbor[0]][neighbor[1]].visited = true 157 | @cells[neighbor[0]][neighbor[1]].recentlyVisited = true 158 | @cells[neighbor[0]][neighbor[1]].visitedAlpha = 1 159 | @location = neighbor 160 | else 161 | unless @location[1] == @height 162 | @location[1] += 1 163 | else 164 | @complete = true 165 | # normal traversal mode 166 | else 167 | neighbor = @getNeighbor(@location[0], @location[1], false) 168 | unless not neighbor 169 | @cells[neighbor[0]][neighbor[1]].visited = true 170 | @cells[neighbor[0]][neighbor[1]].recentlyVisited = true 171 | @cells[neighbor[0]][neighbor[1]].visitedAlpha = 1 172 | @location = neighbor 173 | else 174 | @hunting = true 175 | @location = [0, 0] 176 | 177 | huntInRow: (row) => 178 | # let's visually mark this row as hunted for drawing purposes. 179 | for i in [0..@width] 180 | @cells[i][row].hunted = true 181 | @cells[i][row].huntedAlpha = 1 182 | # now let's actually hunt this row. 183 | for i in [0..@width] 184 | if not @cells[i][row].visited 185 | # if this cell is unvisited, is it next to a cell that IS visited? 186 | if @cells[i-1]?[row]?.visited or 187 | @cells[i+1]?[row]?.visited or 188 | @cells[i]?[row-1]?.visited or 189 | @cells[i]?[row+1]?.visited 190 | return [i, row] 191 | # we couldn't find valid prey. 192 | false 193 | 194 | getNeighbor: (x, y, visited) -> 195 | # 0 1 2 3 - north east south west 196 | # store which sides we've checked. 197 | checkedSides = [] 198 | 199 | # have we found a successful neighbor yet? 200 | until checkedSides.length == 4 201 | sideToCheck = Math.floor(Math.random() * 4) 202 | if sideToCheck in checkedSides 203 | continue 204 | else 205 | # Check if this is a valid direction for us to go. If so, remove the edge. 206 | if sideToCheck == 0 and @cells[x]?[y - 1]?.visited == visited 207 | @cells[x][y].north = false 208 | @cells[x][y - 1].south = false 209 | return [x, y - 1] 210 | else if sideToCheck == 1 and @cells[x + 1]?[y]?.visited == visited 211 | @cells[x][y].east = false 212 | @cells[x + 1][y].west = false 213 | return [x + 1, y] 214 | else if sideToCheck == 2 and @cells[x]?[y + 1]?.visited == visited 215 | @cells[x][y].south = false 216 | @cells[x][y + 1].north = false 217 | return [x, y + 1] 218 | else if sideToCheck == 3 and @cells[x - 1]?[y]?.visited == visited 219 | @cells[x][y].west = false 220 | @cells[x - 1][y].east = false 221 | return [x - 1, y] 222 | 223 | checkedSides.push(sideToCheck) 224 | 225 | # we couldn't find a valid neighbor 226 | false 227 | 228 | class Cell 229 | constructor: (@x, @y) -> 230 | @alpha = 1.0 231 | @visited = false 232 | @north = true 233 | @east = true 234 | @south = true 235 | @west = true 236 | @visitedAlpha = 0 237 | @huntedAlpha = 0 238 | 239 | @recentlyVisited = false 240 | @hunted = false 241 | 242 | update: -> 243 | # slowly deplete the alpha values if they're active 244 | if @recentlyVisited 245 | @visitedAlpha = if @visitedAlpha == 0 then 0 else @visitedAlpha - 0.03 246 | if @visitedAlpha <= 0 247 | @visitedAlpha = 0 248 | @recentlyVisited = false 249 | 250 | if @hunted 251 | @huntedAlpha = if @huntedAlpha == 0 then 0 else @huntedAlpha - 0.03 252 | if @huntedAlpha <= 0 253 | @hunted = false 254 | @huntedAlpha = 0 255 | --------------------------------------------------------------------------------