Quantcast
Channel: Forest - A Simulated Ecosystem - Code Golf Stack Exchange
Viewing all articles
Browse latest Browse all 5

Answer by Blackhole for Forest - A Simulated Ecosystem

$
0
0

AngularJS

Here is my version, which is still a Work In Progress: the code is a bit… well… ugly. And quite slow. I also plan to add more options to parametrize the evolution and to analyse the state of the forest. Comments and amelioration proposals are welcome!

Screenshot of the forest

Demonstration

<div ng-app="ForestApp" ng-controller="ForestController"><form name="parametersForm" ng-hide="evolutionInProgress" autocomplete="off" novalidate><div class="line"><label for="forestSize">Size of the forest:</label><input type="number" ng-model="Parameters.forestSize" id="forestSize" min="5" ng-pattern="/^[0-9]+$/" required /></div><div class="line"><label for="simulationInterval">Number of milliseconds between each tick</label><input type="number" ng-model="Parameters.simulationInterval" id="simulationInterval" min="10" ng-pattern="/^[0-9]+$/" required /></div><div class="line"><label for="animationsEnabled">Animations enabled?<br /><small>(>= 300 ms between each tick is advisable)</small></label><input type="checkbox" ng-model="Parameters.animationsEnabled" id="animationsEnabled" /></div><div class="line"><button ng-disabled="parametersForm.$invalid || evolutionInProgress" ng-click="launchEvolution()">Launch the evolution!</button></div></form><div id="forest" ng-style="{width: side = (20*Parameters.forestSize) +'px', height: side, 'transition-duration': transitionDuration = (Parameters.animationsEnabled ? 0.8*Parameters.simulationInterval : 0) +'ms', '-webkit-transition-duration': transitionDuration}"><div ng-repeat="bear in Forest.bearsList" class="entity entity--bear" ng-style="{left: (20*bear.x) +'px', top: (20*bear.y) +'px'}"></div><div ng-repeat="lumberjack in Forest.lumberjacksList" class="entity entity--lumberjack" ng-style="{left: (20*lumberjack.x) +'px', top: (20*lumberjack.y) +'px'}"></div><div ng-repeat="tree in Forest.treesList" class="entity entity--tree" ng-class="'entity--tree--'+ tree.stage" ng-style="{left: (20*tree.x) +'px', top: (20*tree.y) +'px'}"></div></div><div class="line"><em>Age of the forest:</em><samp>{{floor(Forest.age/12)}} year{{floor(Forest.age/12) > 1 ? 's' : ''}} and {{Forest.age%12}} month{{Forest.age%12 > 1 ? 's' : ''}}</samp></div><div class="line"><em>Number of bears:</em><samp>{{Forest.bearsList.length}}</samp></div><div class="line"><em>Number of lumberjacks:</em><samp>{{Forest.lumberjacksList.length}}</samp></div><br /><div class="line"><em>Number of lumbers collected:</em><samp>{{Forest.numberOfLumbers}}</samp></div><div class="line"><em>Number of mauls:</em><samp>{{Forest.numberOfMauls}}</samp></div></div>
/** @link http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array */function shuffle(array) {    var currentIndex = array.length,        temporaryValue, randomIndex;    // While there remain elements to shuffle...    while (0 !== currentIndex) {        // Pick a remaining element...        randomIndex = Math.floor(Math.random() * currentIndex);        currentIndex -= 1;        // And swap it with the current element.        temporaryValue = array[currentIndex];        array[currentIndex] = array[randomIndex];        array[randomIndex] = temporaryValue;    }    return array;}var forestApp = angular.module('ForestApp', ['ngAnimate']);forestApp.value('Parameters', {    /** @var int[] Maximal number of moves by species */    speed: {        bear: 5,        lumberjack: 3,    },    /** @var int[] Initial percentage of each species in the forest */    initialPercentage: {        bear: 2,        lumberjack: 10,        tree: 50,    },    /** @var int[] Spawing rate, in percentage, of new saplings around an existing tree */    spawningPercentage: {        0: 0,        1: 10,        2: 20,    },    /** @var int[] Age of growth for an existing tree */    ageOfGrowth: {        sapling: 12,        tree: 120,    },    /** @var int[] Lumber collected on an existing tree */    numberOfLumbers: {        tree: 1,        elderTree: 2,    },    /** @var int Size of each side of the forest */    forestSize: 20,    /** @var int Number of milliseconds between each tick (month in the forest) */    simulationInterval: 50,});forestApp.constant('TREE_STAGE', {    SAPLING: 0,    TREE: 1,    ELDER_TREE: 2,});forestApp.factory('Tree', ['Forest', 'Parameters', 'TREE_STAGE', function (Forest, Parameters, TREE_STAGE) {    // Classes which represents a tree    var Tree = function (stage, x, y) {        /** @var TREE_STAGE Current stage of the tree */        this.stage = stage;        /** @var int Current age of the tree, in month */        this.age = 0;        /** @var int X coordinates of the tree */        this.x = x;        /** @var int Y coordinates of the tree */        this.y = y;        this.tick = function () {            if (Math.random() < Parameters.spawningPercentage[this.stage] / 100) {                var freePositionsList = shuffle(Forest.getFreePositionsAround(this.x, this.y));                if (freePositionsList.length > 0) {                    var saplingPosition = freePositionsList[0];                    Tree.create(TREE_STAGE.SAPLING, saplingPosition[0], saplingPosition[1]);                }            }++this.age;            if (this.stage === TREE_STAGE.SAPLING && this.age == Parameters.ageOfGrowth.sapling) {                this.stage = TREE_STAGE.TREE;            } else if (this.stage === TREE_STAGE.TREE && this.age == Parameters.ageOfGrowth.tree) {                this.stage = TREE_STAGE.ELDER_TREE;            }        };        /**         * Remove the entity         */        this.remove = function () {            var index = Forest.treesList.indexOf(this);            Forest.treesList.splice(index, 1);        };    };    Tree.create = function (stage, x, y) {        Forest.add.tree(new Tree(stage, x, y));    };    return Tree;}]);forestApp.factory('Lumberjack', ['Forest', 'Parameters', 'TREE_STAGE', function (Forest, Parameters, TREE_STAGE) {    // Classes which represents a lumberjack    var Lumberjack = function (x, y) {        /** @var int X coordinates of the lumberjack */        this.x = x;        /** @var int Y coordinates of the lumberjack */        this.y = y;        this.tick = function () {            for (movement = Parameters.speed.lumberjack; movement > 0; --movement) {                var positionsList = shuffle(Forest.getPositionsAround(this.x, this.y));                var newPosition = positionsList[0];                this.x = newPosition[0];                this.y = newPosition[1];                var tree = Forest.getTreeAt(this.x, this.y);                if (tree !== null) {                    if (tree.stage === TREE_STAGE.SAPLING) {                        return;                    } else if (tree.stage === TREE_STAGE.TREE) {                        Forest.numberOfLumbers += Parameters.numberOfLumbers.tree;                    } else {                        Forest.numberOfLumbers += Parameters.numberOfLumbers.elderTree;                    }                    tree.remove();                    movement = 0;                };            }        };        /**         * Remove the entity         */        this.remove = function () {            if (Forest.lumberjacksList.length === 1) {                this.x = Math.floor(Math.random() * Parameters.forestSize);                this.y = Math.floor(Math.random() * Parameters.forestSize);            } else {                var index = Forest.lumberjacksList.indexOf(this);                Forest.lumberjacksList.splice(index, 1);            }        };    };    Lumberjack.create = function (x, y) {        Forest.add.lumberjack(new Lumberjack(x, y));    };    return Lumberjack;}]);forestApp.factory('Bear', ['Forest', 'Parameters', function (Forest, Parameters) {    // Classes which represents a bear    var Bear = function (x, y) {        /** @var int X coordinates of the bear */        this.x = x;        /** @var int Y coordinates of the bear */        this.y = y;        this.tick = function () {            for (movement = Parameters.speed.bear; movement > 0; --movement) {                var positionsList = shuffle(Forest.getPositionsAround(this.x, this.y));                var newPosition = positionsList[0];                this.x = newPosition[0];                this.y = newPosition[1];                angular.forEach(Forest.getLumberjacksListAt(this.x, this.y), function (lumberjack) {                    lumberjack.remove();++Forest.numberOfMauls;                    movement = 0;                });            }        };        /**         * Remove the entity         */        this.remove = function () {            var index = Forest.bearsList.indexOf(this);            Forest.bearsList.splice(index, 1);        };    };    Bear.create = function (x, y) {        Forest.add.bear(new Bear(x, y));    };    return Bear;}]);forestApp.service('Forest', ['Parameters', function (Parameters) {    var forest = this;    this.age = 0;    this.numberOfLumbers = 0;    this.numberOfMauls = 0;    this.bearsList = [];    this.lumberjacksList = [];    this.treesList = [];    this.getEntitiesList = function () {        return forest.bearsList.concat(forest.lumberjacksList, forest.treesList);    };    /**     * Age the forest by one month     */    this.tick = function () {        angular.forEach(forest.getEntitiesList(), function (entity) {            entity.tick();        });++forest.age;    };    this.add = {        bear: function (bear) {            forest.bearsList.push(bear);        },        lumberjack: function (lumberjack) {            forest.lumberjacksList.push(lumberjack);        },        tree: function (tree) {            forest.treesList.push(tree);        },    };    /**     * @return Tree|null Tree at this position, or NULL if there is no tree.     */    this.getTreeAt = function (x, y) {        var numberOfTrees = forest.treesList.length;        for (treeId = 0; treeId < numberOfTrees; ++treeId) {            var tree = forest.treesList[treeId];            if (tree.x === x && tree.y === y) {                return tree;            }        }        return null;    };    /**     * @return Lumberjack[] List of the lumberjacks at this position     */    this.getLumberjacksListAt = function (x, y) {        var lumberjacksList = [];        angular.forEach(forest.lumberjacksList, function (lumberjack) {            if (lumberjack.x === x && lumberjack.y === y) {                lumberjacksList.push(lumberjack);            }        });        return lumberjacksList;    };    /**     * @return int[] Positions around this position     */    this.getPositionsAround = function (x, y) {        var positionsList = [            [x - 1, y - 1],            [x, y - 1],            [x + 1, y - 1],            [x - 1, y],            [x + 1, y],            [x - 1, y + 1],            [x, y + 1],            [x + 1, y + 1]        ];        return positionsList.filter(function (position) {            return (position[0] >= 0 && position[1] >= 0 && position[0] < Parameters.forestSize && position[1] < Parameters.forestSize);        });    };    /**     * @return int[] Positions without tree around this position     */    this.getFreePositionsAround = function (x, y) {        var positionsList = forest.getPositionsAround(x, y);        return positionsList.filter(function (position) {            return forest.getTreeAt(position[0], position[1]) === null;        });    };}]);forestApp.controller('ForestController', ['$interval', '$scope', 'Bear', 'Forest', 'Lumberjack', 'Parameters', 'Tree', 'TREE_STAGE', function ($interval, $scope, Bear, Forest, Lumberjack, Parameters, Tree, TREE_STAGE) {    $scope.Forest = Forest;    $scope.Parameters = Parameters;    $scope.evolutionInProgress = false;    $scope.floor = Math.floor;    var positionsList = [];    /**     * Start the evolution of the forest     */    $scope.launchEvolution = function () {        $scope.evolutionInProgress = true;        for (var x = 0; x < Parameters.forestSize; ++x) {            for (var y = 0; y < Parameters.forestSize; ++y) {                positionsList.push([x, y]);            }        }        shuffle(positionsList);        var numberOfBears = Parameters.initialPercentage.bear * Math.pow(Parameters.forestSize, 2) / 100;        for (var bearId = 0; bearId < numberOfBears; ++bearId) {            Bear.create(positionsList[bearId][0], positionsList[bearId][1]);        }        shuffle(positionsList);        var numberOfLumberjacks = Parameters.initialPercentage.lumberjack * Math.pow(Parameters.forestSize, 2) / 100;        for (var lumberjackId = 0; lumberjackId < numberOfLumberjacks; ++lumberjackId) {            Lumberjack.create(positionsList[lumberjackId][0], positionsList[lumberjackId][1]);        }        shuffle(positionsList);        var numberOfTrees = Parameters.initialPercentage.tree * Math.pow(Parameters.forestSize, 2) / 100;        for (var treeId = 0; treeId < numberOfTrees; ++treeId) {            Tree.create(TREE_STAGE.TREE, positionsList[treeId][0], positionsList[treeId][1]);        }        $interval(function () {            Forest.tick();            if (Forest.age % 12 === 0) {                // Hire or fire lumberjacks                if (Forest.numberOfLumbers >= Forest.lumberjacksList.length) {                    shuffle(positionsList);                    var numberOfLumberjacks = Math.floor(Forest.numberOfLumbers / Forest.lumberjacksList.length);                    for (var lumberjackId = 0; lumberjackId < numberOfLumberjacks; ++lumberjackId) {                        Lumberjack.create(positionsList[lumberjackId][0], positionsList[lumberjackId][1]);                    }                } else {                    shuffle(Forest.lumberjacksList);                    Forest.lumberjacksList[0].remove();                }                // Hire or fire bears                if (Forest.numberOfMauls === 0) {                    shuffle(positionsList);                    Bear.create(positionsList[0][0], positionsList[0][1]);                } else {                    Forest.bearsList[0].remove();                }                Forest.numberOfLumbers = 0;                Forest.numberOfMauls = 0;            }        }, Parameters.simulationInterval);    };}]);

Viewing all articles
Browse latest Browse all 5

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>