UNB/ CS/ David Bremner/ teaching/ cs2613/ labs/ CS2613 Lab 13

Background

Overview

When we left off Lab 12, we had a method toString to print our World object as a simple ascii-art map. The general plan is to write a loop that updates the world (i.e. moving some critters around), and then prints it out again.

Implimenting and testing World.turn()

Time
25 minutes
Activity
Small groups
             it("turn",
                function () {
                    var count=0;
                    spyOn(world, 'letAct').and.callFake(function(critter,vector) {count++;});
                    world.turn();
                    expect(count).toBe(4);
                });
             it("checkDestination",
                function () {
                    expect(world.checkDestination({direction: 's'},
                                                  new life.Vector(18,1))).______________________________;
                    expect(world.checkDestination({direction: 'n'},
                                                  new life.Vector(0,0))).toBe(undefined);
                });
             it("letAct",
                function () {
                    var src=new life.Vector(19,1);
                    var dest=_____________________;
                    var bob=world.grid.get(src);
                    spyOn(bob,'act').and.returnValue({type: 'move', direction: 's'});
                    world.letAct(bob, src);
                    expect(world.grid.get(dest)).toEqual(bob);
                });

View objects

Time
25 minutes
Activity
Small groups

A view object encapsulates a particular creature and a current location.

describe("View",
         function () {
             var world = new life.World(plan, {"#": life.Wall, "o": life.BouncingCritter});
             var position=new Vector(15,9);
             it("constructor",
                function () {
                    var view=new View(world, position);
                    expect(view.vector).toEqual(position);
                });
         });
             it("look",
                function () {
                    var view=new View(world, position);
                    expect(view.look("s"))._________;
                });
             it("findAll",
                function () {
                    var view=new View(world, position);
                    var directionNames = [ 'e', 'n', 'ne', 'nw', 's', 'se', 'sw', 'w' ];
                    expect(view.findAll(" ")_______).toEqual(directionNames);
                });
                function () {
                    var view=new View(world, position);
                    expect(view.find(" ")).toBe('s');
                });

Timers

Time
30 minutes
Activity
Demo, group discussion

In this section we look at some of the simplest examples of asynchronous programming in JavaScript. This means that rather than running functions directly, we let the runtime invoke them later triggered by some event.

In order to have some crude simulation of animation, we want to clear the terminal screen between printing the map. It turns out that for most terminals, it suffices to print out the ANSI escape "ESC c" to reset the terminal.

Our first attempt at animation is

var str="";
for (var i=0; i<60; i++) {
    console.log('\033c');
    str+= "*";
    console.log(str);
}

console.log("all done!");

Something is not quite as desired here; in particular if we move the console.log outside the loop, the visual effect is the same. The obvious answer is to delay between iterations. Unlike some programming languages, JavaScript does not define a sleep builtin to pause the current thread. This is mainly because there is only one thread in JavaScript (including node.js). Instead there is built-in suport for timers. Perhaps the simplest (or at least the shortest) way to get what we want is to use setTimeout like the following example:

function loop(i,str) {
    if (i>0) {
        console.log("\033c");
        console.log(str);
        setTimeout(function() { loop(i-1, str+"*"); }, 1000);
    }
}

loop(20,"*");

console.log("all done!");

Something is not quite right here either with the printing of "all done!". Let's trace out a smaller example like loop(3,"*") on the board to try and understand what's going on.

If you don't like the recursive style of the previous example, you can use setInterval, but notice you have to explicitely cancel the timer, or it runs forever.

function animate(iterations) {
    var i=0;
    var str="";
    var timer = null;
    function frame() {
        i++;  str += "#";
        console.log('\033c');
        console.log(str);
        if (i>=iterations)
            clearInterval(timer);
    }
    timer=setInterval(frame,300);
}

animate(20);

Where should we add console.log("all done!")?

Putting the Pieces to together

Time
20 minutes
Activity
Small groups
    var life=require("./life.js");

    var plan = ["############################",
                "#      #    #      o      ##",
                "#                          #",
                "#          #####           #",
                "##         #   #    ##     #",
                "###           ##     #     #",
                "#           ###      #     #",
                "#   ####                   #",
                "#   ##       o             #",
                "# o  #         o       ### #",
                "#    #                     #",
                "############################"];

    var world = new life.World(plan, {"#": life.Wall, "o": life.BouncingCritter});

    for (var i = 0; i < 5; i++) {
        world.turn();
        console.log(world.toString());
    }