Maze solving algorithm using p5.js - html

I have generated a maze using depth first search - recursive backtracker algorithm. I also want to solve, but not getting idea about how to start solving my maze.
I am using p5.js to create my maze, and want to solve my already generated maze.
This is my javascript code for generating maze. You might want to add p5.js cdn in your html file if you want to run this code.
var cols, rows;
var w = 40;
var grid = [];
var current;
var stack = [];
function setup() {
createCanvas(400,400);
cols = floor(width/w);
rows = floor(height/w);
frameRate(5);
for (var j = 0; j<rows; j++){
for (var i = 0; i < cols; i++) {
var cell = new Cell(i,j);
grid.push(cell);
}
}
current = grid[0];
}
function draw(){
background(51);
for (var i = 0; i<grid.length; i++){
grid[i].show();
}
current.visited = true;
current.highlight();
var next = current.checkNeighbours();
if (next) {
next.visited = true;
stack.push(current);
removeWalls(current,next);
current = next;
}
else if(stack.length > 0){
current = stack.pop();
}
}
function index(i,j){
if (i < 0 || j < 0 || i > cols-1 || j > rows-1) {
return -1;
}
return i + j * cols;
}
function Cell(i,j){
this.i = i;
this.j = j;
this.walls = [true,true,true,true];
this.visited = false;
this.checkNeighbours = function(){
var neighbours = [];
var top = grid[index(i, j-1)];
var right = grid[index(i+1, j)];
var bottom = grid[index(i, j+1)];
var left = grid[index(i-1, j)];
if (top && !top.visited){
neighbours.push(top);
}
if (right && !right.visited){
neighbours.push(right);
}
if (bottom && !bottom.visited){
neighbours.push(bottom);
}
if (left && !left.visited){
neighbours.push(left);
}
if (neighbours.length > 0){
var r = floor(random(0, neighbours.length));
return neighbours[r];
}
else{
return undefined;
}
}
this.highlight = function(){
x = this.i*w;
y = this.j*w;
noStroke();
fill(0,0,255,200);
rect(x,y,w,w);
}
this.show = function(){
x = this.i*w;
y = this.j*w;
stroke(255);
if (this.walls[0]){
line(x ,y ,x+w ,y);
}
if (this.walls[1]){
line(x+w ,y ,x+w ,y+w);
}
if (this.walls[2]){
line(x+w ,y+w ,x ,y+w);
}
if (this.walls[3]){
line(x ,y+w ,x ,y)
}
if (this.visited) {
noStroke();
fill(255,0,255,100);
rect(x,y,w,w);
}
}
}
function removeWalls(a,b){
var x = a.i - b.i;
if (x === 1){
a.walls[3] = false;
b.walls[1] = false;
}
else if (x === -1){
a.walls[1] = false;
b.walls[3] = false;
}
var y = a.j - b.j;
if (y === 1){
a.walls[0] = false;
b.walls[2] = false;
}
else if (y === -1){
a.walls[2] = false;
b.walls[0] = false;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>

There are many algorithmsfor solving mazes. One simple way to solve mazes created with the recursive backtracker algorithm is to keep track of the solution as the maze is being generated.
Make the first cell the starting cell and push it onto the solution stack
Make the last cell the goal cell
While the solution stack does not contain the goal cell
if the next neighbor is un-visited push it onto the solution stack
if a cell has no next neighbor pop the solution stack as we are backtracking
When the goal cell is pushed onto the solution stack mark the solution complete
Adapting the questions code so that it also implements the solution algorithm we have:
var cols, rows;
var w = 40;
var grid = [];
var current;
var stack = [];
var solution = [];
var goal;
var solutionComplete;
function setup() {
createCanvas(400,400);
cols = floor(width/w);
rows = floor(height/w);
frameRate(5);
for (var j = 0; j<rows; j++){
for (var i = 0; i < cols; i++) {
var cell = new Cell(i,j);
grid.push(cell);
}
}
current = grid[0];
grid[grid.length - 1].goal = true;
solution.push(grid[0]);
}
function draw(){
background(51);
for (var i = 0; i<grid.length; i++){
grid[i].show();
}
current.visited = true;
current.highlight();
var next = current.checkNeighbours();
if (next) {
if (!next.visited){
if (!solutionComplete){
solution.push(next);
if (next.goal){
solutionComplete = true;
}
}
}
next.visited = true;
stack.push(current);
removeWalls(current,next);
current = next;
}
else if(stack.length > 0){
current = stack.pop();
if (!solutionComplete){
solution.pop();
}
}
if (solutionComplete){
for (let i = 0; i < solution.length; i++){
solution[i].solutionCell = true;
}
}
}
function index(i,j){
if (i < 0 || j < 0 || i > cols-1 || j > rows-1) {
return -1;
}
return i + j * cols;
}
function Cell(i,j){
this.i = i;
this.j = j;
this.walls = [true,true,true,true];
this.visited = false;
this.goal = false;
this.solutionCell = false;
this.checkNeighbours = function(){
var neighbours = [];
var top = grid[index(i, j-1)];
var right = grid[index(i+1, j)];
var bottom = grid[index(i, j+1)];
var left = grid[index(i-1, j)];
if (top && !top.visited){
neighbours.push(top);
}
if (right && !right.visited){
neighbours.push(right);
}
if (bottom && !bottom.visited){
neighbours.push(bottom);
}
if (left && !left.visited){
neighbours.push(left);
}
if (neighbours.length > 0){
var r = floor(random(0, neighbours.length));
return neighbours[r];
}
else{
return undefined;
}
}
this.highlight = function(){
x = this.i*w;
y = this.j*w;
noStroke();
fill(0,0,255,200);
rect(x,y,w,w);
}
this.show = function(){
x = this.i*w;
y = this.j*w;
stroke(255);
if (this.walls[0]){
line(x ,y ,x+w ,y);
}
if (this.walls[1]){
line(x+w ,y ,x+w ,y+w);
}
if (this.walls[2]){
line(x+w ,y+w ,x ,y+w);
}
if (this.walls[3]){
line(x ,y+w ,x ,y)
}
if (this.goal){
noStroke();
fill(0,255,0,100);
rect(x,y,w,w);
}
else if (this.solutionCell){
noStroke();
fill(255,0,0,100);
rect(x,y,w,w);
}else if(this.visited) {
noStroke();
fill(255,0,255,100);
rect(x,y,w,w);
}
}
}
function removeWalls(a,b){
var x = a.i - b.i;
if (x === 1){
a.walls[3] = false;
b.walls[1] = false;
}
else if (x === -1){
a.walls[1] = false;
b.walls[3] = false;
}
var y = a.j - b.j;
if (y === 1){
a.walls[0] = false;
b.walls[2] = false;
}
else if (y === -1){
a.walls[2] = false;
b.walls[0] = false;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.min.js"></script>
It would not be difficult to break the maze generation and solution implementations apart so that the maze is completely generated before the solution is determined but unless there is a restriction that forces us to solve a completed maze it makes sense to build the solution along with the maze.

Sorry about this this is very related but coding train a coding youtuber made a maze generating algorithm, but i don't know if it used depth first search

Related

Character keeps falling when it shouldn't be

I am doing an assessment task for school in as3 and I have made a feature that will let the character fall if it is not standing on top of something but the boolean variable falling keeps being on when it is not supposed to. I have figured out that what causes it to stop for a short time and then fall again past what it is standing on is that the velocity valuable increases past the floor it is on so I reset the velocity value in the code in another spot but that does not solve the issue of the falling boolean being true when it is not supposed too.
I have put a comment at the temporary solution it is in the gameEngine function in the if(falling) statement
// Imports
import flash.events.KeyboardEvent;
import flash.events.Event;
// Constants that can be edited
const grav:Number = 0.05;
const jumpPow:Number = 15;
const walkingDistance = 50;
// Variables
var jumpVel:Number = jumpPow;
var velocity:Number = 0;
var dt:Number = 0;
var movingCount:Number = 0;
var newX:Number = 0;
var newY:Number = 0;
var charCornerHit:Number = 0; // 0 = TL 1 = TR 2 = BR 3 = BL (clockwise)
var jumping:Boolean = false;
var falling:Boolean = false;
var movingRight:Boolean = false;
var movingLeft:Boolean = false;
var hitDetVal:Boolean = false; // false
var floorHit:Boolean = false;
var object1Hit:Boolean = false;
var cCnrTL:Array = new Array(char.x, char.y);
var cCnrTR:Array = new Array(char.x + char.width, char.y);
var cCnrBR:Array = new Array(char.x + char.width, char.y + char.height);
var cCnrBL:Array = new Array(char.x, char.y + char.height);
var charCorners:Array = new Array(cCnrTL, cCnrTR, cCnrBR, cCnrBL); // Clockwise
addEventListener(Event.ENTER_FRAME, gameEngine);
function gameEngine(evt:Event):void{
stage.addEventListener(KeyboardEvent.KEY_DOWN, moveCharacter);
charCorners[0][0] = char.x;
charCorners[0][1] = char.y;
charCorners[1][0] = char.x + char.width;
charCorners[1][1] = char.y;
charCorners[2][0] = char.x + char.width;
charCorners[2][1] = char.y + char.height;
charCorners[3][0] = char.x;
charCorners[3][1] = char.y + char.height;
//trace(char.y);
// Check if char is standing on something
// Supposed to only check when character is not falling
if (falling == false) {
//trace(char.y + char.height + 1)
hitDetection(0, char.y + char.height + 1)
if (hitDetVal == false) {
falling = true;
hitDetVal = false;
trace("not standing");
}
else {trace("standing on something");}
}
// Move char loop
if (movingRight){
if (movingCount == walkingDistance) {
movingCount = 0
movingRight = false;
} else {
char.x+=1
movingCount += 1;
}
} else if (movingLeft) {
if (movingCount == walkingDistance) {
movingCount = 0
movingLeft = false;
} else {
char.x-=1
movingCount += 1;
}
}
//trace(velocity)
if (falling) {
dt += 1
velocity = velocity + grav*dt
hitDetection(0, velocity)
if (hitDetVal) {
falling = false;
hitDetVal = false;
// TEMPORARY SOLUTION - Stopped character from falling past the floor but still ran falling condition
//velocity = 0;
//dt = 0;
if (floorHit) {
if (char.y < floor.y){
char.y = floor.y - char.height;
floorHit = false;
trace("Char Stopped falling")
}
} else if (object1Hit) {
if (char.y - char.height < object1.y){
char.y = object1.y - char.height;
object1Hit = false;
trace("Char Stopped falling")
}
}
} else {
char.y += velocity;
}
} else {
if (jumping) {
}
velocity = 0;
dt = 0;
}
}
function moveCharacter(event:KeyboardEvent):void{
if(event.keyCode == Keyboard.LEFT){
if(movingRight){
movingRight = false;
trace("STOPPED")
} else {
movingCount = 0
movingLeft = true;
trace("LEFT");
}
}
if(event.keyCode == Keyboard.RIGHT){
if(movingLeft){
movingLeft = false;
trace("STOPPED")
} else {
movingCount = 0
movingRight = true;
trace("RIGHT");
}
}
if(event.keyCode == Keyboard.UP){
jumping = true;
trace("UP");
}
}
function hitDetection(distX:Number, distY:Number) {
for (var i:uint = 0; i < charCorners.length; i++) {
newX = charCorners[i][0] + distX;
newY = charCorners[i][1] + distY;
if (floor.hitTestPoint(newX, newY, true)){
hitDetVal = true;
floorHit = true;
charCornerHit = i;
break;
} else if (object1.hitTestPoint(newX, newY, true)){
hitDetVal = true;
object1Hit = true;
charCornerHit = i;
break;
}
}
}```
It was a simple logic error occurring here
hitDetection(0, char.y + char.height + 1)
I had made adjustments for the location by adding char.y to the value when my hitDetection function already made an adjustment to each corner of my character. Only took ~3 hours to figure out

Action Script 3.0: how can I type other languages in the array or how can I use the other languages in grids

Here is my code. This is my word search game ...In word list array ...these are my questions to find in grid in the game...my questions how can I type other languages in the array or how can I use the other languages in grids
import flash.display.*;
import flash.text.*;
import flash.geom.Point;
import flash.events.*;
import flash.net.FileFilter;
import flash.filters.GlowFilter;
const puzzleSize:uint = 20;
const spacing:Number = 24;
const outlineSize:Number = 20;
const offsetoint = new Point(162.9,179.7);
const offsettoint = new Point(300,265);
const spacingg:Number = 36;
const letterFormat:TextFormat = new TextFormat("Bamini",18,0x000000,true,false,false,null,null,TextFormatAlign.CENTER);
// words and grid
var wordList:Array;
var usedWords:Array;
var grid:Array;
// game state
var dragModetring;
var startPoint,endPointoint;
var numFound:int;
// sprites
var gameSpriteprite;
var outlineSpriteprite;
var oldOutlineSpriteprite;
var letterSpritesprite;
var wordsSpriteprite;
startWordSearch();
function startWordSearch() {
**// word list**
wordList = ("Lion,Tiger,Cheetah,Panther".split(",";
// set up the sprites
gameSprite = new Sprite();
addChild(gameSprite);
oldOutlineSprite = new Sprite();
gameSprite.addChild(oldOutlineSprite);
outlineSprite = new Sprite();
gameSprite.addChild(outlineSprite);
letterSprites = new Sprite();
gameSprite.addChild(letterSprites);
wordsSprite = new Sprite();
gameSprite.addChild(wordsSprite);
// array of letters
var letters:Array = placeLetters();
// array of sprites
grid = new Array();
for(var x:int=0;x<puzzleSize;x++) {
grid[x] = new Array();
for(var y:int=0;y<puzzleSize;y++) {
// create new letter field and sprite
var newLetter:TextField = new TextField();
newLetter.defaultTextFormat = letterFormat;
newLetter.x = x*spacing + offset.x;
newLetter.y = y*spacing + offset.y;
newLetter.width = spacing;
newLetter.height = spacing;
newLetter.text = letters[x][y];
newLetter.selectable = false;
var newLetterSpriteprite = new Sprite();
newLetterSprite.addChild(newLetter);
letterSprites.addChild(newLetterSprite);
grid[x][y] = newLetterSprite;
// add event listeners
newLetterSprite.addEventListener(MouseEvent.MOUSE_DOWN, clickLetter);
newLetterSprite.addEventListener(MouseEvent.MOUSE_OVER, overLetter);
}
}
// stage listener
stage.addEventListener(MouseEvent.MOUSE_UP, mouseRelease);
// create word list fields and sprites
for(var i:int=0;i<usedWords.length;i++) {
//var myglow:GlowFilter=new GlowFilter();
//myglow.color = 0x0000FF;
//myglow.blurX=5;
//myglow.blurY=5;
//myglow.strength=200;
var newWord:TextField = new TextField();
newWord.defaultTextFormat = letterFormat;
newWord.x = 700;
newWord.y = i*spacingg+offsett.y;
newWord.width =140;
newWord.height =spacing;
newWord.text = usedWords[i];
//newWord.filters=[myglow];
newWord.selectable = false;
wordsSprite.addChild(newWord);
}
// set game state
dragMode = "none";
numFound = 0;
}
// place the words in a grid of letters
function placeLetters():Array {
// create empty grid
var letters:Array = new Array();
for(var x:int=0;x<puzzleSize;x++) {
letters[x] = new Array();
for(var y:int=0;y<puzzleSize;y++) {
letters[x][y] = "*";
}
}
// make copy of word list
var wordListCopy:Array = wordList.concat();
usedWords = new Array();
// make 1000 attempts to add words
var repeatTimes:int = 1000;
repeatLoop:while (wordListCopy.length > 0) {
if (repeatTimes-- <= 0) break;
// pick a random word, location and direction
var wordNum:int = Math.floor(Math.random()*wordListCopy.length);
var wordtring = wordListCopy[wordNum].toUpperCase();
x = Math.floor(Math.random()*puzzleSize);
y = Math.floor(Math.random()*puzzleSize);
var dx:int = Math.floor(Math.random()*3)-1;
var dy:int = Math.floor(Math.random()*3)-1;
if ((dx == 0) && (dy == 0)) continue repeatLoop;
// check each spot in grid to see if word fits
letterLoop:for (var j:int=0;j<word.length;j++) {
if ((x+dx*j < 0) || (y+dy*j < 0) || (x+dx*j >= puzzleSize) || (y+dy*j >= puzzleSize)) continue repeatLoop;
var thisLettertring = letters[x+dx*j][y+dy*j];
if ((thisLetter != "*" && (thisLetter != word.charAt(j))) continue repeatLoop;
}
// insert word into grid
insertLoop:for (j=0;j<word.length;j++) {
letters[x+dx*j][y+dy*j] = word.charAt(j);
}
// remove word from list
wordListCopy.splice(wordNum,1);
usedWords.push(word);
}
// fill rest of grid with random letters
for(x=0;x<puzzleSize;x++) {
for(y=0;y<puzzleSize;y++) {
if (letters[x][y] == "*" {
letters[x][y] = String.fromCharCode(65+Math.floor(Math.random()*26));
}
}
}
return letters;
}
// player clicks down on a letter to start
function clickLetter(event:MouseEvent) {
var lettertring = event.currentTarget.getChildAt(0).text;
startPoint = findGridPoint(event.currentTarget);
dragMode = "drag";
}
// player dragging over letters
function overLetter(event:MouseEvent) {
if (dragMode == "drag" {
endPoint = findGridPoint(event.currentTarget);
// if valid range, show outline
outlineSprite.graphics.clear();
if (isValidRange(startPoint,endPoint)) {
drawOutline(outlineSprite,startPoint,endPoint,0x66CCFF);
}
}
}
// mouse released
function mouseRelease(event:MouseEvent) {
if (dragMode == "drag" {
dragMode = "none";
outlineSprite.graphics.clear();
// get word and check it
if (isValidRange(startPoint,endPoint)) {
var word = getSelectedWord();
checkWord(word);
}
}
}
// when a letter is clicked, find and return the x and y location
function findGridPoint(letterSpritebject)oint {
// loop through all sprites and find this one
for(var x:int=0;x<puzzleSize;x++) {
for(var y:int=0;y<puzzleSize;y++) {
if (grid[x][y] == letterSprite) {
return new Point(x,y);
}
}
}
return null;
}
// determine if range is in the same row, column, or a 45 degree diagonal
function isValidRange(p1,p2oint):Boolean {
if (p1.x == p2.x) return true;
if (p1.y == p2.y) return true;
if (Math.abs(p2.x-p1.x) == Math.abs(p2.y-p1.y)) return true;
return false;
}
// draw a thick line from one location to another
function drawOutline(sprite,p1,p2oint,c:Number) {
var offoint = new Point(offset.x+spacing/2, offset.y+spacing/2);
s.graphics.lineStyle(outlineSize,c);
s.graphics.moveTo(p1.x*spacing+off.x ,p1.y*spacing+off.y);
s.graphics.lineTo(p2.x*spacing+off.x ,p2.y*spacing+off.y);
}
// find selected letters based on start and end points
function getSelectedWord()tring {
// determine dx and dy of selection, and word length
var dx = endPoint.x-startPoint.x;
var dy = endPoint.y-startPoint.y;
var wordLength:Number = Math.max(Math.abs(dx),Math.abs(dy))+1;
// get each character of selection
var wordtring = "";
for(var i:int=0;i<wordLength;i++) {
var x = startPoint.x;
if (dx < 0) x -= i;
if (dx > 0) x += i;
var y = startPoint.y;
if (dy < 0) y -= i;
if (dy > 0) y += i;
word += grid[x][y].getChildAt(0).text;
}
return word;
}
// check word against word list
function checkWord(wordtring) {
// loop through words
for(var i:int=0;i<usedWords.length;i++) {
// compare word
if (word == usedWords[i].toUpperCase()) {
foundWord(word);
}
// compare word reversed
var reverseWordtring = word.split("".reverse().join("";
if (reverseWord == usedWords[i].toUpperCase()) {
foundWord(reverseWord);
}
}
}
// word found, remove from list, make outline permanent
function foundWord(wordtring) {
// draw outline in permanent sprite
drawOutline(oldOutlineSprite,startPoint,endPoint,0xE01212);
// find text field and set it to gray
for(var i:int=0;i<wordsSprite.numChildren;i++) {
if (TextField(wordsSprite.getChildAt).text.toUpperCase() == word) {
TextField(wordsSprite.getChildAt).textColor = 0xFF0000;
}
}
// see if all have been found
numFound++;
if (numFound ==usedWords.length) {
endGame();
}
}
function endGame() {
gotoAndStop("gameover";
cleanUp();
Timee.stop();
}
function cleanUp() {
removeChild(gameSprite);
gameSprite = null;
grid = null;
vv=null;
}

Autocomplete search for any word in the sentence

I'm using an autocomplete for my flash app. The autocomplete uses an external text file.
When I'm typing the first word of the sentence, it display all the sentences that begins with this word.
Is it possible to display all sentence that have this word (and not just the begining of the sentence) ?
Exemple :
I've got two phrases : "I'm going to school" and " I'm going to look for him".
I would like to be able to type "school" and that it displays the first sentence.
Do you know how I can do that ?
For now, I have to type "I'm going to s" in order to display the first sentence.
Here's my code :
urlLoader.load(new URLRequest("test.txt"));
urlLoader.addEventListener(Event.COMPLETE, loadComplete);
inputField.addEventListener(KeyboardEvent.KEY_UP, suggest);
function loadComplete(e:Event):void
{
suggestions = e.target.data.split(",");
}
function suggest(e:KeyboardEvent):void
{
suggested = [];
for (var i:int = 0; i < textfields.length; i++)
{
removeChild(textfields[i]);
}
textfields = [];
for (var j:int = 0; j < suggestions.length; j++)
{
if (suggestions[j].indexOf(inputField.text.toLowerCase()) == 0)
{
var term:TextField = new TextField();
term.width = 300;
term.height = 20;
term.x = 70;
term.y = (20 * suggested.length) + 314;
term.border = true;
term.borderColor = 0x353535;
term.background = true;
term.backgroundColor = 0xFF9900;
term.textColor = 0x4C311D;
term.defaultTextFormat = format;
term.addEventListener(MouseEvent.MOUSE_UP, useWord);
term.addEventListener(MouseEvent.MOUSE_OVER, hover);
term.addEventListener(MouseEvent.MOUSE_OUT, out);
term.addEventListener(MouseEvent.CLICK, tellMe);
addChild(term);
textfields.push(term);
suggested.push(suggestions[j]);
term.text = suggestions[j];
}
}
if (inputField.length == 0)
{
suggested = [];
for (var k:int = 0; k < textfields.length; k++)
{
removeChild(textfields[k]);
}
textfields = [];
}
if(e.keyCode == Keyboard.DOWN && currentSelection < textfields.length-1)
{
currentSelection++;
textfields[currentSelection].textColor = 0x4C311D;
}
if(e.keyCode == Keyboard.UP && currentSelection > 0)
{
currentSelection--;
textfields[currentSelection].textColor = 0x4C311D;
}
if(e.keyCode == Keyboard.ENTER)
{
inputField.text = textfields[currentSelection].text;
suggested = [];
for (var l:int = 0; l < textfields.length; l++)
{
removeChild(textfields[l]);
}
textfields = [];
currentSelection = 0;
}
}
function useWord(e:MouseEvent):void
{
inputField.text = e.target.text;
suggested = [];
for (var i:int = 0; i < textfields.length; i++)
{
removeChild(textfields[i]);
}
textfields = [];
}
Thank you
Change the condition from:
suggestions[j].indexOf(inputField.text.toLowerCase()) == 0
to
suggestions[j].indexOf(inputField.text.toLowerCase()) != -1

customer layout with drag-drop support

Evtim wrote a greate customer FlowLayout, and Maxim also have a nice customer Horizontal Multiline Layout but they don't support drag-drop. When define customer
Layout which extends LayoutBase with drag-drop supporting, user should implements or override which functions?
I had worked out, following is my customer layout based on Horizontal Multiline Layouy of Maxim:
public class HorizontalMultilineLayout extends LayoutBase {
private var _rowCount:int = -1;
private var _columnCount:int = -1;
//container width
private var lastWidth:Number = -1;
private var _horizontalGap:Number = 6;
public function get horizontalGap():Number
{
return _horizontalGap;
}
public function set horizontalGap(value:Number):void
{
if (value == _horizontalGap)
return;
_horizontalGap = value;
invalidateTargetSizeAndDisplayList();
}
private var _verticalGap:Number = 6;
public function get verticalGap():Number
{
return _verticalGap;
}
public function set verticalGap(value:Number):void
{
if (value == _verticalGap)
return;
_verticalGap = value;
invalidateTargetSizeAndDisplayList();
}
private var _verticalAlign:String = VerticalAlign.TOP;
[Inspectable(category="General", enumeration="top,bottom,middle", defaultValue="top")]
public function get verticalAlign():String
{
return _verticalAlign;
}
public function set verticalAlign(value:String):void
{
if (_verticalAlign == value)
return;
_verticalAlign = value;
invalidateTargetSizeAndDisplayList();
}
private var _paddingLeft:Number = 0;
public function get paddingLeft():Number
{
return _paddingLeft;
}
public function set paddingLeft(value:Number):void
{
if (_paddingLeft == value)
return;
_paddingLeft = value;
invalidateTargetSizeAndDisplayList();
}
private var _paddingRight:Number = 0;
public function get paddingRight():Number
{
return _paddingRight;
}
public function set paddingRight(value:Number):void
{
if (_paddingRight == value)
return;
_paddingRight = value;
invalidateTargetSizeAndDisplayList();
}
private var _paddingTop:Number = 0;
public function get paddingTop():Number
{
return _paddingTop;
}
public function set paddingTop(value:Number):void
{
if (_paddingTop == value)
return;
_paddingTop = value;
invalidateTargetSizeAndDisplayList();
}
private var _paddingBottom:Number = 0;
public function get paddingBottom():Number
{
return _paddingBottom;
}
public function set paddingBottom(value:Number):void
{
if (_paddingBottom == value)
return;
_paddingBottom = value;
invalidateTargetSizeAndDisplayList();
}
override public function measure():void
{
if (lastWidth == -1) {
return;
}
var measuredWidth:Number = 0;
var measuredMinWidth:Number = 0;
var measuredHeight:Number = 0;
var measuredMinHeight:Number = 0;
var layoutTarget:GroupBase = target;
var n:int = layoutTarget.numElements;
var element:ILayoutElement;
var i:int;
var width:Number = layoutTarget.explicitWidth;
if (isNaN(width) && lastWidth != -1)
width = lastWidth;
if (isNaN(width)) // width is not defined by parent or user
{
// do not specify measuredWidth and measuredHeight to real
// values because in fact we can layout at any width or height
for (i = 0; i < n; i++)
{
element = useVirtualLayout ? layoutTarget.getVirtualElementAt(i) : layoutTarget.getElementAt(i);
if (!element || !element.includeInLayout)
continue;
measuredWidth = Math.ceil(element.getPreferredBoundsWidth());
measuredHeight = Math.ceil(element.getPreferredBoundsHeight());
break;
}
measuredMinWidth = measuredWidth;
measuredMinHeight = measuredHeight;
}
else
{
// calculate lines based on width
var currentLineWidth:Number = 0;
var currentLineHeight:Number = 0;
var lineNum:int = 1;
for (i = 0; i < n; i++)
{
element = useVirtualLayout ? layoutTarget.getVirtualElementAt(i) : layoutTarget.getElementAt(i);
if (!element || !element.includeInLayout)
continue;
var widthWithoutPaddings:Number = width - _paddingLeft - _paddingRight;
var elementWidth:Number = Math.ceil(element.getPreferredBoundsWidth());
if (currentLineWidth == 0 ||
currentLineWidth + _horizontalGap + elementWidth <= widthWithoutPaddings)
{
currentLineWidth += elementWidth + (currentLineWidth == 0 ? 0 : _horizontalGap);
currentLineHeight = Math.max(currentLineHeight, Math.ceil(element.getPreferredBoundsHeight()));
}
else
{
measuredHeight += currentLineHeight;
lineNum++;
currentLineWidth = elementWidth;
currentLineHeight = Math.ceil(element.getPreferredBoundsHeight());
}
}
measuredHeight += currentLineHeight;
if (lineNum > 1)
measuredHeight += _verticalGap * (lineNum - 1);
// do not set measuredWidth to real value because really we can
// layout at any width
for (i = 0; i < n; i++)
{
element = useVirtualLayout ? layoutTarget.getVirtualElementAt(i) : layoutTarget.getElementAt(i);
if (!element || !element.includeInLayout)
continue;
measuredWidth =
measuredMinWidth = Math.ceil(element.getPreferredBoundsWidth());
break;
}
measuredMinHeight = measuredHeight;
}
layoutTarget.measuredWidth = measuredWidth + _paddingLeft + _paddingRight;
layoutTarget.measuredMinWidth = measuredMinWidth + _paddingLeft + _paddingRight;
layoutTarget.measuredHeight = measuredHeight + _paddingTop + _paddingBottom;
layoutTarget.measuredMinHeight = measuredMinHeight + _paddingTop + _paddingBottom;
}
override public function updateDisplayList(width:Number, height:Number):void
{
resetRowAndColumn();
var layoutTarget:GroupBase = target;
var n:int = layoutTarget.numElements;
var element:ILayoutElement;
var i:int;
// calculate lines based on width
var x:Number = _paddingLeft;
var y:Number = _paddingTop;
var maxLineHeight:Number = 0;
var elementCounter:int = 0;
var positions:Vector.<Point> = new Vector.<Point>();
for (i = 0; i < n; i++)
{
element = useVirtualLayout ? layoutTarget.getVirtualElementAt(i) : layoutTarget.getElementAt(i);
if (!element || !element.includeInLayout)
continue;
var elementWidth:Number = Math.ceil(element.getPreferredBoundsWidth());
if (x == _paddingLeft || x + _horizontalGap + elementWidth <= width - _paddingRight) {
if (elementCounter > 0)
x += _horizontalGap;
positions[i] = new Point(x, y);
element.setLayoutBoundsSize(NaN, NaN);
maxLineHeight = Math.max(maxLineHeight, Math.ceil(element.getPreferredBoundsHeight()));
//calculate column count
if(!startNewLine) {
_columnCount++;
}
}else {
startNewLine = true;
_rowCount++;
x = _paddingLeft;
y += _verticalGap + maxLineHeight;
maxLineHeight = Math.ceil(element.getPreferredBoundsHeight());
positions[i] = new Point(x, y);
element.setLayoutBoundsSize(NaN, NaN);
}
x += elementWidth;
elementCounter++;
}
// verticalAlign and setLayoutBoundsPosition() for elements
var yAdd:Number = 0;
var yDifference:Number = height - (y + maxLineHeight + _paddingBottom);
if (_verticalAlign == VerticalAlign.MIDDLE)
yAdd = Math.round(yDifference / 2);
else if (_verticalAlign == VerticalAlign.BOTTOM)
yAdd = Math.round(yDifference);
for (i = 0; i < n; i++)
{
element = useVirtualLayout ? layoutTarget.getVirtualElementAt(i) : layoutTarget.getElementAt(i);
if (!element || !element.includeInLayout)
continue;
var point:Point = positions[i];
point.y += yAdd;
element.setLayoutBoundsPosition(point.x, point.y);
}
// if width changed then height will change too - remeasure
if (lastWidth == -1 || lastWidth != width)
{
lastWidth = width;
invalidateTargetSizeAndDisplayList();
}
//trace("rowCount: ---", _rowCount, " ---columnCount: ----", _columnCount);
}
private var startNewLine:Boolean = false;
private function resetRowAndColumn():void {
var num:int = target.numElements;
if(num >0) {
_rowCount = 1;
_columnCount = 0;
} else {
_rowCount = _columnCount = 0;
}
startNewLine = false;
}
private function invalidateTargetSizeAndDisplayList():void
{
var g:GroupBase = target;
if (!g)
return;
g.invalidateSize();
g.invalidateDisplayList();
}
private function calculateDropCellIndex(x:Number, y:Number):Array {
var xStart:Number = x - paddingLeft;
var yStart:Number = y - paddingTop;
var column:int = Math.floor(xStart / (columnWidth + _horizontalGap));
var row:int = Math.floor(yStart / (rowHeight + _verticalGap));
// Check whether x is closer to left column or right column:
var midColumnLine:Number;
var midRowLine:Number;
midColumnLine = (column + 1) * (columnWidth + _horizontalGap) - _horizontalGap - columnWidth / 2;
// Mid-line is at the middle of the gap between the rows
midRowLine = (row + 1) * (rowHeight + _verticalGap) - _verticalGap / 2;
if (xStart > midColumnLine)
column++;
if (yStart > midRowLine)
row++;
// Limit row and column, if any one is too far from the drop location
// And there is white space
if (column > _columnCount || row > _rowCount)
{
row = _rowCount;
column = _columnCount;
}
if (column < 0)
column = 0;
if (row < 0)
row = 0;
if (row >= _rowCount)
row = _rowCount - 1;
return [row, column];
}
override protected function calculateDropIndex(x:Number, y:Number):int {
var result:Array = calculateDropCellIndex(x, y);
var row:int = result[0];
var column:int = result[1];
var index:int = row * _columnCount + column;
var count:int = target.numElements;
if (index > count) {
index = count;
}
return index;
}
private function get columnWidth():Number {
return typicalLayoutElement.getLayoutBoundsWidth();
}
private function get rowHeight():Number {
return typicalLayoutElement.getLayoutBoundsHeight();
}
//NOTE:we do NOT use contentWidth
override protected function calculateDropIndicatorBounds(dropLocation:DropLocation):Rectangle {
var result:Array = calculateDropCellIndex(dropLocation.dropPoint.x, dropLocation.dropPoint.y);
var row:int = result[0];
var column:int = result[1];
var count:int = target.numElements;
// The last row may be only partially full,
// don't drop beyond the last column.
if (row * _columnCount + column > count)
column = count - (_rowCount - 1) * _columnCount;
var x:Number;
var y:Number;
var dropIndicatorElement:IVisualElement;
var emptySpace:Number; // empty space between the elements
// Start with the dropIndicator dimensions, in case it's not
// an IVisualElement
var width:Number = dropIndicator.width;
var height:Number = dropIndicator.height;
emptySpace = (0 < _horizontalGap ) ? _horizontalGap : 0;
var emptySpaceLeft:Number = column * (columnWidth + _horizontalGap) - emptySpace;
// Special case - if we have negative gap and we're the last column,
// adjust the emptySpaceLeft
if (_horizontalGap < 0 && (column == _columnCount || count == dropLocation.dropIndex))
emptySpaceLeft -= _horizontalGap;
width = emptySpace;
height = rowHeight;
// Special case - if we have negative gap and we're not the last
// row, adjust the height
if (_verticalGap < 0 && row < _rowCount - 1)
height += _verticalGap + 1;
if (dropIndicator is IVisualElement)
{
dropIndicatorElement = IVisualElement(dropIndicator);
width = Math.max(Math.min(width,
dropIndicatorElement.getMaxBoundsWidth(false)),
dropIndicatorElement.getMinBoundsWidth(false));
}
x = emptySpaceLeft + Math.round((emptySpace - width) / 2) + paddingLeft;
// Allow 1 pixel overlap with container border
//x = Math.max(-1, Math.min(target.contentWidth - width + 1, x));
//NOTE:we do NOT use contentWidth
x = Math.max(-1, Math.min(target.width - width + 1, x));
y = row * (rowHeight + _verticalGap) + paddingTop;
return new Rectangle(x, y, width, height);
}
}

Getting 'TypeError' response for Gmail Meter Script.

I'm getting the following error:
"TypeError: Cannot read property "0.0" from null. (line227)"
I've been looking through the documentation here: https://developers.google.com/apps-script/articles/gmail-stats#section1
But haven't been able to troubleshoot.
Line227:
variables.dayOfEmailsReceived[day]++;
Sorry, new to stackoverflow but any help greatly appreciated.
Reference:
var BATCH_SIZE = 50;
var ss = SpreadsheetApp.getActiveSpreadsheet();
// If you are using several email addresses, list them in the following variable
// - eg 'romain.vialard#gmail.com,romain.vialard#example.com'
var aliases = 'romain.vialard#euromed-management.com';
function activityReport() {
var status = ScriptProperties.getProperty("status");
if (status == null) {
// If the script is triggered for the first time, init
init_();
}
else {
status = Utilities.jsonParse(status);
var previousMonth = new Date(new Date().getFullYear(), new Date().getMonth() - 1, 1).getMonth();
if (status == null || (status.customReport == false && status.previousMonth != previousMonth)) {
init_();
fetchEmails_(status.customReport);
}
// If report not sent, continue to work on the report
else if (status.reportSent == "no") {
fetchEmails_(status.customReport);
}
}
}
function fetchEmails_(customReport) {
var variables = Utilities.jsonParse(ScriptProperties.getProperty("variables"));
if (!customReport) {
var query = "after:" + variables.year + "/" + (variables.previousMonth) + "/31";
query += " before:" + new Date().getYear() + "/" + (variables.previousMonth + 1) + "/31";
}
else {
var query = "after:" + Utilities.formatDate(new Date(variables.startDate), variables.userTimeZone, 'yyyy/MM/dd');
query += " before:" + Utilities.formatDate(new Date(variables.endDate), variables.userTimeZone, 'yyyy/MM/dd');
}
query += " in:anywhere -label:sms -label:call-log -label:chats -label:spam -filename:ics";
query += " -from:maestro.bounces.google.com -from:unified-notifications.bounces.google.com -from:docs.google.com";
query += " -from:group.calendar.google.com -from:apps-scripts-notifications#google.com";
query += " -from:sites.bounces.google.com -from:noreply -from:notify -from:notification";
var startDate = new Date(variables.startDate).getTime();
var endDate = new Date(variables.endDate).getTime();
var conversations = GmailApp.search(query, variables.range, BATCH_SIZE);
variables.nbrOfConversations += conversations.length;
var sheets = ss.getSheets();
var people = sheets[0].getDataRange().getValues();
var record = [];
for (var i = 0; i < conversations.length; i++) {
Utilities.sleep(1000);
var conversationId = conversations[i].getId();
var firstMessageSubject = conversations[i].getFirstMessageSubject();
var starred = false;
if (conversations[i].hasStarredMessages()) {
variables.nbrOfConversationsStarred++;
starred = true;
}
var important = false;
if (conversations[i].isImportant()) {
variables.nbrOfConversationsMarkedAsImportant++;
important = true;
}
var location = "";
var labels = conversations[i].getLabels();
var nbrOfLabels = labels.length;
if (nbrOfLabels == 0) {
if (conversations[i].isInInbox()) {
variables.nbrOfConversationsInInbox++;
location += "Inbox,";
}
else if (conversations[i].isInTrash()) {
variables.nbrOfConversationsInTrash++;
location += "Trashed,";
}
else {
variables.nbrOfConversationsArchived++;
location = "Archived";
}
}
else {
variables.nbrOfConversationsInLabels++;
for (var j = 0; j < nbrOfLabels; j++) {
location += labels[j].getName() + ",";
}
}
var youReplied = false;
var youStartedTheConversation = false;
var someoneAnswered = false;
var messages = conversations[i].getMessages();
var nbrOfMessages = messages.length;
variables.nbrOfEmailsPerConversation[nbrOfMessages]++;
for (var j = 0; j < 10; j++) {
if (variables.topThreads[j][1] < nbrOfMessages) {
variables.topThreads.splice(j, 0, [firstMessageSubject, nbrOfMessages]);
variables.topThreads.pop();
j = 10;
}
}
var timeOfFirstMessage = 0;
var waitingTime = 0;
for (var j = 0; j < nbrOfMessages; j++) {
var process = true;
var date = messages[j].getDate();
var month = date.getMonth();
if (customReport) {
if (date.getTime() < startDate || date.getTime() > endDate) {
process = false;
}
}
else {
if (month != variables.previousMonth) {
process = false;
}
}
if (process) {
//////////////////////////////////
// Fetch sender of each emails
//////////////////////////////////
var from = messages[j].getFrom().replace(/"[^"]*"/g,'');
if (from.match(/</) != null) {
from = from.match(/<([^>]*)/)[1];
}
var time = Utilities.formatDate(date, variables.userTimeZone, "H");
var day = Utilities.formatDate(date, variables.userTimeZone, "d") - 1;
// Use function from Utilities file
variables = countSendsPerDaysOfWeek_(variables, date, from);
var body = messages[j].getBody();
// Use function from Utilities file
var resultsFromCalcMessagesLength = calcMessagesLength_(variables, body, from);
variables = resultsFromCalcMessagesLength[0];
var messageLength = resultsFromCalcMessagesLength[1];
var cc = messages[j].getCc().replace(/"[^"]*"/g,'').split(/,/);
for (var k = 0; k < cc.length; k++) {
if (cc[k].match(/</) != null) {
cc[k] = cc[k].match(/<([^>]*)/)[1];
}
}
var reg = new RegExp(from, 'i');
if ((variables.user + aliases).search(reg) != -1) {
if (j == 0) {
youStartedTheConversation = true;
timeOfFirstMessage = date.getTime();
}
if (j > 0 && !youStartedTheConversation) {
if (!youReplied) {
youReplied = true;
// Use function from Utilities file
variables = calcWaitingTime_(variables, date, timeOfFirstMessage, youStartedTheConversation);
}
}
variables.nbrOfEmailsSent++;
variables.timeOfEmailsSent[time]++;
variables.dayOfEmailsSent[day]++;
if (customReport) {
variables.monthOfEmailsSent[month]++;
}
var sharedWithTheOutsideWorld = false;
var to = messages[j].getTo().replace(/"[^"]*"/g,'').split(/,/);
for (var k = 0; k < to.length; k++) {
if (to[k].match(/</) != null) {
to[k] = to[k].match(/<([^>]*)/)[1];
}
if (to[k].search(variables.companyname) == -1){
sharedWithTheOutsideWorld = true;
}
var found = false;
for (var l = 0; l < people.length; l++) {
if (to[k] == people[l][0]) {
people[l][2]++;
found = true;
}
}
if (!found) {
people.push([to[k], 0, 1]);
}
}
if(!sharedWithTheOutsideWorld){
variables.sharedInternally++;
}
}
else {
if (j == 0) {
timeOfFirstMessage = date.getTime();
}
else if (youStartedTheConversation && !someoneAnswered) {
someoneAnswered = true;
// Use function from Utilities file
variables = calcWaitingTime_(variables, date, timeOfFirstMessage, youStartedTheConversation);
}
var found = false;
for (var k = 0; k < people.length; k++) {
if (from == people[k][0]) {
people[k][1]++;
found = true;
}
}
if (!found) {
people.push([from, 1, 0]);
}
var to = messages[j].getTo().replace(/"[^"]*"/g,'');
var checkSendToYou = false;
var aliasesTemp = new Array(variables.user).concat(aliases.split(','));
for(var k = 0; k < aliasesTemp.length; k++){
if(aliasesTemp[k] != ''){
var reg = new RegExp(aliasesTemp[k], 'i');
if (to.search(reg) != -1) {
checkSendToYou = true;
}
}
}
if(checkSendToYou)variables.sentDirectlyToYou++;
var sharedWithTheOutsideWorld = false;
to = to.split(/,/);
for (var k = 0; k < to.length; k++) {
if (to[k].match(/</) != null) {
to[k] = to[k].match(/<([^>]*)/)[1];
}
if (to[k].search(variables.companyname) == -1){
sharedWithTheOutsideWorld = true;
}
}
if(sharedWithTheOutsideWorld == false && from.search(variables.companyname) != -1){
variables.sharedInternally++;
}
variables.nbrOfEmailsReceived++;
variables.timeOfEmailsReceived[time]++;
variables.dayOfEmailsReceived[day]++;
if (customReport) {
variables.monthOfEmailsReceived[month]++;
}
}
if (to != null) {
to = to.toString();
}
if (cc != null) {
cc = cc.toString();
}
var dayOfWeek = Utilities.formatDate(date, variables.userTimeZone, "EEEE");
record.push([date, dayOfWeek, firstMessageSubject, from, to, cc, messageLength, location]);
}
}
if (youStartedTheConversation) {
variables.nbrOfConversationsStartedByYou++;
}
if (youReplied) {
variables.nbrOfConversationsYouveRepliedTo++;
}
}
variables.range += BATCH_SIZE;
ScriptProperties.setProperty("variables", Utilities.jsonStringify(variables));
sheets[0].getRange(1, 1, people.length, 3).setValues(people);
if (record[0] != undefined && sheets[1].getMaxRows() < 38000) {
sheets[1].getRange(sheets[1].getLastRow() + 1, 1, record.length, record[0].length).setValues(record);
}
if (conversations.length < BATCH_SIZE) {
sendReport_(variables);
}
}
It's a timezone issue. Go to your Gmail Meter file on your Google Drive account and then:
File -> Spreadsheet settings...
Change the timezone to your current time zone. I assume your Gmail account changes timezones automatically, but not your spreadsheet.
The error indicates that and day is 0 variables.dayOfEmailsReceived is null. It's not possible to refer to an index of null. variables.dayOfEmailsReceived is created in the init_() function, so check that you have that function defined and are using it correctly.