const MARGIN = 25; const WALL_RIGHT = 1, WALL_DOWN = 2; const STATUS_GENERATING = 0, STATUS_PLAYING = 1, STATUS_SOLVED = 2, STATUS_ABORTED = -1; function Maze(n) { this.n = n; this.status = STATUS_GENERATING; this.wall_length = Math.floor((g.getHeight()-2*MARGIN)/n); this.total_length = this.wall_length*n; this.margin = Math.floor((g.getHeight()-this.total_length)/2); this.ball_x = 0; this.ball_y = 0; // This voodoo is needed because otherwise // bottom line widgets (like digital clock) // disappear during maze generation Bangle.drawWidgets(); g.setColor(g.theme.fg); for (let i=0; i<=n; i++) { g.drawRect( this.margin, this.margin+i*this.wall_length, g.getWidth()-this.margin, this.margin+i*this.wall_length ); g.drawRect( this.margin+i*this.wall_length, this.margin, this.margin+i*this.wall_length, g.getHeight() - this.margin ); } this.walls = new Uint8Array(n*n); this.groups = new Uint8Array(n*n); for (let cell = 0; cell0 && !(this.walls[n*(ball_r-1)+ball_c]&WALL_DOWN)) { next_y--; } else if (dy>0 && ball_r<(this.n-1) && !(this.walls[n*ball_r+ball_c]&WALL_DOWN)) { next_y++; } else if (dx<0 && ball_c>0 && !(this.walls[n*ball_r+ball_c-1]&WALL_RIGHT)) { next_x--; } else if (dx>0 && ball_c<(this.n-1) && !(this.walls[n*ball_r+ball_c]&WALL_RIGHT)) { next_x++; } else { return false; } } this.clearCell(ball_r, ball_c); if (this.ball_x%this.wall_length) { this.clearCell(ball_r, ball_c+1); } if (this.ball_y%this.wall_length) { this.clearCell(ball_r+1, ball_c); } this.ball_x = next_x; this.ball_y = next_y; this.drawBall(this.ball_x, this.ball_y); if (this.ball_x==(n-1)*this.wall_length && this.ball_y==(n-1)*this.wall_length) { this.status = STATUS_SOLVED; } return true; }; this.try_move_horizontally = function(accel_x) { if (accel_x>0.15) { return this.move(-1, 0); } else if (accel_x<-0.15) { return this.move(1, 0); } return false; }; this.try_move_vertically = function(accel_y) { if (accel_y<-0.15) { return this.move(0,1); } else if (accel_y>0.15) { return this.move(0,-1); } return false; }; this.tick = function() { accel = Bangle.getAccel(); if (this.ball_x%this.wall_length) { this.try_move_horizontally(accel.x); } else if (this.ball_y%this.wall_length) { this.try_move_vertically(accel.y); } else { if (Math.abs(accel.x)>Math.abs(accel.y)) { // prefer horizontally if (!this.try_move_horizontally(accel.x)) { this.try_move_vertically(accel.y); } } else { // prefer vertically if (!this.try_move_vertically(accel.y)) { this.try_move_horizontally(accel.x); } } } }; this.clearCell(0,0); this.clearCell(n-1,n-1); this.drawBall(0,0); this.status = STATUS_PLAYING; } function timeToText(t) { // Courtesy of stopwatch app let hrs = Math.floor(t/3600000); let mins = Math.floor(t/60000)%60; let secs = Math.floor(t/1000)%60; let tnth = Math.floor(t/100)%10; let text; if (hrs === 0) text = ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2) + "." + tnth; else text = ("0"+hrs) + ":" + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2); return text; } let aborting = false; let start_time = 0; let duration = 0; let maze=null; let mazeMenu = { "": { "title": "Maze size", "selected": 1 }, "Easy (8x8)": function() { E.showMenu(); maze = new Maze(8); }, "Medium (10x10)": function() { E.showMenu(); maze = new Maze(10); }, "Hard (14x14)": function() { E.showMenu(); maze = new Maze(14); }, "< Exit": function() { setTimeout(load, 100); } // timeout voodoo prevents deadlock }; g.reset(); Bangle.loadWidgets(); Bangle.drawWidgets(); Bangle.setLocked(false); Bangle.setLCDTimeout(0); E.showMenu(mazeMenu); let maze_interval = setInterval( function() { if (maze) { if (digitalRead(BTN1) || maze.status==STATUS_ABORTED) { maze = null; start_time = duration = 0; aborting = false; setTimeout(function() {E.showMenu(mazeMenu); }, 100); return; } if (!start_time) { start_time = Date.now(); } if (maze.status==STATUS_PLAYING) { maze.tick(); } if (maze.status==STATUS_SOLVED && !duration) { duration = Date.now()-start_time; g.setFontAlign(0,0).setColor(g.theme.fg); g.setFont("Vector",18); g.drawString(`Solved ${maze.n}X${maze.n} in\n ${timeToText(duration)} \nBtn1 to play again`, g.getWidth()/2, g.getHeight()/2, true); } } }, 25);