Code viewer for Mind: Complex Mind (clone by Dhe...
// Cloned by Dheeraj Putta on 3 Nov 2020 from Mind "Complex Mind" by Starter user 
// Please leave this clone trail here.




// =================================================================================================
// Sample Mind for more complex starter World  
// =================================================================================================

// World tells us agent position and enemy position
// World does not tell us of existence of walls
// if return invalid move (not empty square) World just ignores it and we miss a turn 
// #### Created code ####

const depth = 3;

function Queue() {
    this.items = []

    this.enqueue = function (elm) {
        this.items.push(elm);
    }

    this.dequeue = function () {
        var temp = this.items[0];
        this.items.shift();
        return temp;
    }
}

function Node(data) {
    this.data = data;
    this.parent = null;
    this.children = [];
    this.score = null;
}

function Tree(data) {
    this.root = new Node(data);

    this.populateTree = function () {
        var queue = new Queue();

        this.addChildren(this.root);

        queue.enqueue(this.root);

        currentNode = queue.dequeue();

        var d = 0;

        while (currentNode && d < depth - 1) {
            for (var i = 0; i < currentNode.children.length; i++) {
                this.addChildren(currentNode.children[i]);
                queue.enqueue(currentNode.children[i]);
            }

            currentNode = queue.dequeue();
            d++;
        }
    };

    this.addChildren = function (parent) {
        for (var i = 0; i < parent.data.neighbors.length; i++) {
            var child = new Node(parent.data.neighbors[i])
            child.parent = parent;
            parent.children.push(child);
        }
    };

    // this.addChildren = function (parent) {
    //     for (var i = 0; i < parent.data.neighbors.length; i++) {
    //         if (parent.parent && parent.data.neighbors[i] != parent.parent.data) {
    //             var child = new Node(parent.data.neighbors[i])
    //             child.parent = parent;
    //             parent.children.push(child);
    //         }
    //         else if (!parent.parent) {
    //             var child = new Node(parent.data.neighbors[i])
    //             child.parent = parent;
    //             parent.children.push(child);
    //         }
    //     }
    // }

    this.miniMax = function (ei, ej) {
        (function recurse(node, d) {
            for (var i = 0; i < node.children.length; i++) {
                recurse(node.children[i], d + 1);
            }

            if (node.children.length == 0) {
                node.score = heuristicMind(node.data.i, node.data.j, ei, ej);
            }
            else if (d % 2 == 0) {
                node.score = Math.max(...node.children.map(x => x.score));
            }
            else {
                node.score = Math.min(...node.children.map(x => x.score));
            }
        })(this.root, 0)
    };

    this.getBest = function () {
        var score = -1;
        var best = null;
        for (var i = 0; i < this.root.children.length; i++) {
            if (score < this.root.children[i].score) {
                best = this.root.children[i];
                score = this.root.children[i].score;
            }
        }
        return best;
    };
    // this.addChildren = function (parent) {
    //     for (var i = 0; i < parent.data.neighbors.length; i++) {
    //         if (parent.parent && parent.data.neighbors[i] != parent.parent.data) {
    //             var child = new Node(parent.data.neighbors[i])
    //             child.parent = parent;
    //             parent.children.push(child);
    //         }
    //     }
    // }

    // this.removeParents = function (child) {
    //     var current = child;
    //     for (var i = 0; i < 4; i++) {
    //         try {
    //             removeFromArray(child.children, current.parent);
    //         }
    //         catch (err) {

    //         }
    //         current = current.parent;
    //     }
    // }
}

function heuristicMind(ai, aj, ei, ej) {
    if (!trapMove(ai, aj)) {
        var x = new THREE.Vector3(ai, 0, aj);
        var y = new THREE.Vector3(ei, 0, ej);
        return (x.manhattanDistanceTo(y));
    }
    else {
        return 0;
    }
}

function removeFromArray(arr, elt) {
    // Could use indexOf here instead to be more efficient
    arr.splice(arr.indexOf(elt), 1);
}

function occupiedMind(i, j) {
    if (GRID[i][j].type == GRID_WALL) return true;		// fixed objects	 
    if (GRID[i][j].type == GRID_MAZE) return true;

    return false;
}

function trapMove(i, j) { // detect if agent will become trapped
    var walls = [];

    //up
    walls.push(i < gridsize - 1 && occupiedMind(i + 1, j));

    //down
    walls.push(i > 0 && occupiedMind(i - 1, j));

    //right
    walls.push(j < gridsize - 1 && occupiedMind(i, j + 1));

    //left
    walls.push(j > 0 && occupiedMind(i, j - 1));

    if (walls.reduce((a, b) => a + b, 0) >= 3) {
        return true;
    }
    else {
        return false;
    }
}

AB.mind.getAction = function (x)		// x is an array of [ ai, aj, ei, ej ]
{
    var ai = x[0];
    var aj = x[1];
    var ei = x[2];
    var ej = x[3];

    // if strictly move away, will get stuck at wall, so introduce randomness

    var tree = new Tree(GRID[ai][aj]);
    tree.populateTree();
    tree.miniMax(ei, ej);
    var move = tree.getBest().data
    console.log(tree);
    console.log("Agent move: ", move);

    // if MiniMax move will cause trap then or agent is moving to previous start location then pick a random move
    if (trapMove(move.i, move.j) || (AB.step > 0 && agentLastVisited.reduce((x, y) => x && [move.i, move.j].includes(y), true))) {
        var randomMoves = [...Array(GRID[ai][aj].neighbors.length).keys()];
        while (true) {
            var rand = AB.randomElementOfArray(randomMoves);
            move = GRID[ai][aj].neighbors[rand];
            if (trapMove(move.i, move.j)) {
                removeFromArray(randomMoves, rand);
            }
            else {
                return (move);
            }
        }
    }
    else {
        return (move);
    }
};