Code viewer for World: Cat and Mouse (clone by Ch...
'use strict';
AB.clockTick = 100, AB.maxSteps = 1e3, AB.screenshotStep = 50;
const show3d = true;
const TEXTURE_WALL = "/uploads/jfajou/TronCircuitry.jpg";
const TEXTURE_MAZE = "/uploads/jfajou/TronVector.jpg";
const TEXTURE_AGENT = "/uploads/starter/pacman.jpg";
const TEXTURE_ENEMY = "/uploads/jfajou/Ghost.jpg";
const MUSIC_BACK = "/uploads/starter/Defense.Line.mp3";
const SOUND_ALARM = "/uploads/starter/air.horn.mp3";
const gridsize = 50;
const NOBOXES = Math.trunc(gridsize * gridsize / 10);
const squaresize = 100;
const MAXPOS = gridsize * squaresize;
const SKYCOLOR = 14548957;
const startRadiusConst = .8 * MAXPOS;
const maxRadiusConst = 10 * MAXPOS;
ABHandler.MAXCAMERAPOS = maxRadiusConst, ABHandler.GROUNDZERO = true;
const SKYBOX_ARRAY = ["/uploads/starter/sky_pos_z.jpg", "/uploads/starter/sky_neg_z.jpg", "/uploads/starter/sky_pos_y.jpg", "/uploads/starter/sky_neg_y.jpg", "/uploads/starter/sky_pos_x.jpg", "/uploads/starter/sky_neg_x.jpg"];
const ACTION_LEFT = 0;
const ACTION_RIGHT = 1;
const ACTION_UP = 2;
const ACTION_DOWN = 3;
const ACTION_STAYSTILL = 4;
const GRID_BLANK = 0;
const GRID_WALL = 1;
const GRID_MAZE = 2;
var BOXHEIGHT;
var theagent;
var theenemy;
var wall_texture;
var agent_texture;
var enemy_texture;
var maze_texture;
var ei;
var ej;
var ai;
var aj;
var badsteps;
var goodsteps;
var startNode;
var endNode;
/** @type {!Array} */
var GRID = new Array(gridsize);
/** @type {!Array} */
var trappedSet = [];
/** @type {!Array} */
var winnerSet = [];
/** @type {boolean} */
var isASolution = false;
/** @type {!Array} */
var grid = new Array(gridsize);
/**
 * @return {undefined}
 */
function loadResources() {
    var mockObjectLoader = new THREE.TextureLoader;
    var outbound_chart = new THREE.TextureLoader;
    var inbound_chart = new THREE.TextureLoader;
    var snippetFrame = new THREE.TextureLoader;
    mockObjectLoader.load(TEXTURE_WALL, function (texture) {
        texture.minFilter = THREE.LinearFilter;
        /** @type {!Object} */
        wall_texture = texture;
        if (asynchFinished()) {
            initScene();
        }
    });
    outbound_chart.load(TEXTURE_AGENT, function (texture) {
        texture.minFilter = THREE.LinearFilter;
        /** @type {!Object} */
        agent_texture = texture;
        if (asynchFinished()) {
            initScene();
        }
    });
    inbound_chart.load(TEXTURE_ENEMY, function (texture) {
        texture.minFilter = THREE.LinearFilter;
        /** @type {!Object} */
        enemy_texture = texture;
        if (asynchFinished()) {
            initScene();
        }
    });
    snippetFrame.load(TEXTURE_MAZE, function (texture) {
        texture.minFilter = THREE.LinearFilter;
        /** @type {!Object} */
        maze_texture = texture;
        if (asynchFinished()) {
            initScene();
        }
    });
}
/**
 * @return {?}
 */
function asynchFinished() {
    return !!(wall_texture && agent_texture && enemy_texture && maze_texture);
}
/**
 * @param {number} x
 * @param {number} y
 * @return {?}
 */
function occupied(x, y) {
    return ei == x && ej == y || (ai == x && aj == y || (GRID[x][y] == GRID_WALL || GRID[x][y] == GRID_MAZE));
}
/**
 * @param {number} e
 * @param {number} t
 * @return {?}
 */
function translate(e, t) {
    var globalBonePosition = new THREE.Vector3;
    return globalBonePosition.y = 0, globalBonePosition.x = e * squaresize - MAXPOS / 2, globalBonePosition.z = t * squaresize - MAXPOS / 2, globalBonePosition;
}
/**
 * @return {undefined}
 */
function initScene() {
    var sphere;
    var light;
    /** @type {number} */
    x = 0;
    for (; x < gridsize; x++) {
        /** @type {!Array} */
        GRID[x] = new Array(gridsize);
    }
    /** @type {number} */
    x = 0;
    for (; x < gridsize; x++) {
        /** @type {number} */
        y = 0;
        for (; y < gridsize; y++) {
            if (0 == x || x == gridsize - 1 || 0 == y || y == gridsize - 1) {
                GRID[x][y] = GRID_WALL;
                sphere = new THREE.BoxGeometry(squaresize, BOXHEIGHT, squaresize);
                (light = new THREE.Mesh(sphere)).material = new THREE.MeshBasicMaterial({
                    map: wall_texture
                });
                light.position.copy(translate(x, y));
                ABWorld.scene.add(light);
            } else {
                GRID[x][y] = GRID_BLANK;
            }
        }
    }
    /** @type {number} */
    var i = 1;
    for (; i <= NOBOXES; i++) {
        x = AB.randomIntAtoB(1, gridsize - 2);
        y = AB.randomIntAtoB(1, gridsize - 2);
        GRID[x][y] = GRID_MAZE;
        sphere = new THREE.BoxGeometry(squaresize, BOXHEIGHT, squaresize);
        (light = new THREE.Mesh(sphere)).material = new THREE.MeshBasicMaterial({
            map: maze_texture
        });
        light.position.copy(translate(x, y));
        ABWorld.scene.add(light);
    }
    do {
        x = AB.randomIntAtoB(1, gridsize - 2);
        y = AB.randomIntAtoB(1, gridsize - 2);
    } while (occupied(x, y));
    ei = x;
    ej = y;
    sphere = new THREE.BoxGeometry(squaresize, BOXHEIGHT, squaresize);
    (theenemy = new THREE.Mesh(sphere)).material = new THREE.MeshBasicMaterial({
        map: enemy_texture
    });
    ABWorld.scene.add(theenemy);
    drawEnemy();
    do {
        x = AB.randomIntAtoB(1, gridsize - 2);
        y = AB.randomIntAtoB(1, gridsize - 2);
    } while (occupied(x, y));
    ai = x;
    aj = y;
    sphere = new THREE.BoxGeometry(squaresize, BOXHEIGHT, squaresize);
    (theagent = new THREE.Mesh(sphere)).material = new THREE.MeshBasicMaterial({
        map: agent_texture
    });
    ABWorld.scene.add(theagent);
    drawAgent();
    ABWorld.scene.background = (new THREE.CubeTextureLoader).load(SKYBOX_ARRAY, function () {
        ABWorld.render();
        AB.removeLoading();
        /** @type {boolean} */
        AB.runReady = true;
    });
    /** @type {number} */
    var x = 0;
    for (; x < gridsize; x++) {
        /** @type {!Array} */
        grid[x] = new Array(gridsize);
    }
    /** @type {number} */
    x = 0;
    for (; x < gridsize; x++) {
        /** @type {number} */
        var y = 0;
        for (; y < gridsize; y++) {
            /** @type {!Node} */
            grid[x][y] = new Node(x, y);
        }
    }
    /** @type {number} */
    x = 0;
    for (; x < gridsize; x++) {
        /** @type {number} */
        y = 0;
        for (; y < gridsize; y++) {
            grid[x][y].addNeighbors(grid);
            if (1 == grid[x][y].canBeStoppedHere) {
                /** @type {boolean} */
                isASolution = true;
            }
        }
    }
    if (0 == isASolution) {
        /** @type {boolean} */
        AB.abortRun = true;
        console.log("There is no square available for the agent to be trapped, so we can finish here!");
    }
}
/**
 * @return {undefined}
 */
function drawEnemy() {
    theenemy.position.copy(translate(ei, ej));
    ABWorld.lookat.copy(theenemy.position);
}
/**
 * @return {undefined}
 */
function drawAgent() {
    theagent.position.copy(translate(ai, aj));
    ABWorld.follow.copy(theagent.position);
}
/**
 * @param {number} a
 * @param {number} b
 * @return {undefined}
 */
function Node(a, b) {
    /** @type {number} */
    this.i = a;
    /** @type {number} */
    this.j = b;
    /** @type {number} */
    this.g = 0;
    /** @type {number} */
    this.h = 0;
    /** @type {number} */
    this.f = this.g + this.h;
    /** @type {!Array} */
    this.neighbors = [];
    this.previous = void 0;
    if (GRID[a][b] == GRID_BLANK) {
        /** @type {boolean} */
        this.wall = false;
    } else {
        /** @type {boolean} */
        this.wall = true;
    }
    /** @type {boolean} */
    this.canBeStoppedHere = false;
    /**
     * @param {!Object} arr
     * @return {undefined}
     */
    this.addNeighbors = function (arr) {
        var arom;
        var i = this.i;
        var j = this.j;
        /** @type {number} */
        var r = 0;
        if (i < gridsize - 1) {
            this.neighbors.push(arr[i + 1][j]);
            if (1 == arr[i + 1][j].wall) {
                r++;
            } else {
                arom = arr[i + 1][j];
            }
        }
        if (i > 0) {
            this.neighbors.push(arr[i - 1][j]);
            if (1 == arr[i - 1][j].wall) {
                r++;
            } else {
                arom = arr[i - 1][j];
            }
        }
        if (j < gridsize - 1) {
            this.neighbors.push(arr[i][j + 1]);
            if (1 == arr[i][j + 1].wall) {
                r++;
            } else {
                arom = arr[i][j + 1];
            }
        }
        if (j > 0) {
            this.neighbors.push(arr[i][j - 1]);
            if (1 == arr[i][j - 1].wall) {
                r++;
            } else {
                arom = arr[i][j - 1];
            }
        }
        if (3 == r && 0 == arr[i][j].wall) {
            console.log("***Can be stopped at " + i + ", " + j);
            /** @type {boolean} */
            this.canBeStoppedHere = true;
            trappedSet.push(arr[i][j]);
            winnerSet.push(arom);
        }
        if (i > 0 && j > 0) {
            this.neighbors.push(arr[i - 1][j - 1]);
        }
        if (i < gridsize - 1 && j > 0) {
            this.neighbors.push(arr[i + 1][j - 1]);
        }
        if (i > 0 && j < gridsize - 1) {
            this.neighbors.push(arr[i - 1][j + 1]);
        }
        if (i < gridsize - 1 && j < gridsize - 1) {
            this.neighbors.push(arr[i + 1][j + 1]);
        }
    };
}
/**
 * @param {!Object} a
 * @param {!Object} b
 * @return {?}
 */
function getDist(a, b) {
    /** @type {number} */
    var sqsum = Math.pow(a.i - b.i, 2) + Math.pow(a.j - b.j, 2);
    return Math.sqrt(ssum);
}
/**
 * @return {undefined}
 */
function moveLogicalEnemy() {
    var value;
    /** @type {!Array} */
    var values = [];
    /** @type {!Array} */
    var key = [];
    startNode = grid[ei][ej];
    endNode = grid[ai][aj];
    values.push(startNode);
    for (; values.length > 0;) {
        /** @type {number} */
        var j = 0;
        /** @type {number} */
        var i = 0;
        for (; i < values.length; i++) {
            if (values[i].f < values[j].f) {
                /** @type {number} */
                j = i;
            }
        }
        if ((value = values[j]) == endNode) {
            break;
        }
        /** @type {number} */
        i = values.length - 1;
        for (; i >= 0; i--) {
            if (values[i] == value) {
                values.splice(i, 1);
            }
        }
        key.push(value);
        var n = value.neighbors;
        /** @type {number} */
        i = 0;
        for (; i < n.length; i++) {
            var node = n[i];
            if (!key.includes(node) && !node.wall) {
                var cost = value.g + getDist(node, value);
                /** @type {boolean} */
                var d = false;
                if (values.includes(node)) {
                    if (cost < node.g) {
                        node.g = cost;
                        /** @type {boolean} */
                        d = true;
                    }
                } else {
                    node.g = cost;
                    /** @type {boolean} */
                    d = true;
                    values.push(node);
                }
                if (d) {
                    node.h = getDist(node, endNode);
                    node.previous = value;
                }
            }
        }
    }
    var current = value;
    for (; current.previous && (0, current.previous != startNode);) {
        current = current.previous;
    }
    if (current !== endNode) {
        ei = current.i;
        ej = current.j;
    } else {
        if (trappedSet.includes(endNode)) {
            var origin = winnerSet[trappedSet.indexOf(endNode)];
            if (startNode.neighbors.includes(origin)) {
                console.log("*****In the TRAP!!!!");
                console.log("Winner location " + trappedSet.indexOf(endNode));
                ei = origin.i;
                ej = origin.j;
            } else {
                moveToRandomNeighbour();
            }
        } else {
            moveToRandomNeighbour();
        }
    }
}
/**
 * @return {undefined}
 */
function moveToRandomNeighbour() {
    var i;
    var neighbors = startNode.neighbors;
    do {
        i = AB.randomIntAtoB(0, neighbors.length - 1);
    } while (occupied(neighbors[i].i, neighbors[i].j));
    ei = neighbors[i].i;
    ej = neighbors[i].j;
    console.log("New end Node is " + ei + "," + ej);
}
/**
 * @param {string} opt_parentWin
 * @return {undefined}
 */
function moveLogicalAgent(opt_parentWin) {
    var i = ai;
    var charsetBitSize = aj;
    if (opt_parentWin == ACTION_LEFT) {
        i--;
    } else {
        if (opt_parentWin == ACTION_RIGHT) {
            i++;
        } else {
            if (opt_parentWin == ACTION_UP) {
                charsetBitSize++;
            } else {
                if (opt_parentWin == ACTION_DOWN) {
                    charsetBitSize--;
                }
            }
        }
    }
    if (!occupied(i, charsetBitSize)) {
        ai = i;
        aj = charsetBitSize;
    }
}
/** @type {!Array} */
var OURKEYS = [37, 38, 39, 40];
/**
 * @param {!Event} event
 * @return {?}
 */
function ourKeys(event) {
    return OURKEYS.includes(event.keyCode);
}
/**
 * @param {!Event} event
 * @return {?}
 */
function keyHandler(event) {
    return !AB.runReady || (!ourKeys(event) || (37 == event.keyCode && moveLogicalAgent(ACTION_LEFT), 38 == event.keyCode && moveLogicalAgent(ACTION_DOWN), 39 == event.keyCode && moveLogicalAgent(ACTION_RIGHT), 40 == event.keyCode && moveLogicalAgent(ACTION_UP), event.stopPropagation(), event.preventDefault(), false));
}
/**
 * @return {?}
 */
function badstep() {
    return Math.abs(ei - ai) < 2 && Math.abs(ej - aj) < 2;
}
/**
 * @return {?}
 */
function agentBlocked() {
    return occupied(ai - 1, aj) && occupied(ai + 1, aj) && occupied(ai, aj + 1) && occupied(ai, aj - 1);
}
/**
 * @param {string} opt_parentWin
 * @return {undefined}
 */
function updateStatusBefore(opt_parentWin) {
    var default_favicon = AB.world.getState();
    AB.msg(" Step: " + AB.step + " &nbsp; x = (" + default_favicon.toString() + ") &nbsp; a = (" + opt_parentWin + ") ");
}
/**
 * @return {undefined}
 */
function updateStatusAfter() {
    var default_favicon = AB.world.getState();
    /** @type {number} */
    var e_total = goodsteps / AB.step * 100;
    AB.msg(" &nbsp; y = (" + default_favicon.toString() + ") <br> Bad steps: " + badsteps + " &nbsp; Good steps: " + goodsteps + " &nbsp; Score: " + e_total.toFixed(2) + "% ", 2);
}
AB.world.newRun = function () {
    AB.loadingScreen();
    /** @type {boolean} */
    AB.runReady = false;
    /** @type {number} */
    badsteps = 0;
    /** @type {number} */
    goodsteps = 0;
    BOXHEIGHT = squaresize;
    ABWorld.init3d(startRadiusConst, maxRadiusConst, 14548957);
    loadResources();
    /** @type {function(!Event): ?} */
    document.onkeydown = keyHandler;
}, AB.world.getState = function () {
    return [ai, aj, ei, ej];
}, AB.world.takeAction = function (optionString) {
    updateStatusBefore(optionString);
    moveLogicalAgent(optionString);
    if (AB.step % 2 == 0) {
        moveLogicalEnemy();
    }
    if (badstep()) {
        badsteps++;
    } else {
        goodsteps++;
    }
    drawAgent();
    drawEnemy();
    updateStatusAfter();
    if (agentBlocked()) {
        /** @type {boolean} */
        AB.abortRun = true;
        /** @type {number} */
        goodsteps = 0;
        musicPause();
        soundAlarm();
    }
}, AB.world.endRun = function () {
    musicPause();
    if (0 == isASolution) {
        AB.msg(" <br> <font color=red> <B> The Agent cannot be trapped. The game has ended. </B> </font>   ", 3);
    } else {
        if (AB.abortRun) {
            AB.msg(" <br> <font color=red> <B> Agent trapped. Final score zero. </B> </font>   ", 3);
        } else {
            AB.msg(" <br> <font color=green> <B> Run over. </B> </font>   ", 3);
        }
    }
}, AB.world.getScore = function () {
    /** @type {number} */
    var ipw = goodsteps / AB.maxSteps * 100;
    return Math.round(100 * ipw) / 100;
};
var backmusic = AB.backgroundMusic(MUSIC_BACK);
/**
 * @return {undefined}
 */
function musicPlay() {
    backmusic.play();
}
/**
 * @return {undefined}
 */
function musicPause() {
    backmusic.pause();
}
/**
 * @return {undefined}
 */
function soundAlarm() {
    (new Audio(SOUND_ALARM)).play();
};