107 comments

[ 2.5 ms ] story [ 184 ms ] thread
(comment deleted)
Nitpick: it breaks the browser's back button
Sure does
Yea, the super wide screen is super annoying. Is there some reason for the big width? A little stealth at work would be nice, hard to hide something that huge.
Another nitpick: it's 1360 px wide.
Yeah, I just had a little difficulty with that. It doesn't fit on my crappy projector.
came to the comments to say this
Slightly related question: are there any standard workloads and tools out there for experimenting with elevator programming?

My apartment building has a utility elevator and two normal elevators. Each floor has two sets of call buttons. These have been programmed in different ways over the years, and I've always been curious if there was a good way to quantify the changes.

This is incredible. I've been successfully nerd-sniped away from work. Also nice touch w/ the time-scale factors being Fibonacci numbers :) Great work.
This is awesome, however is there any way to get better syntax errors? Damnit Jim, I'm a C programmer not a Javascript coder!
Try looking in the dev tools? I've been able to debug a few runtime errors this way.
I got to Challenge #5 by just having the elevators go to every floor like in the first example. It'd be nice to have an earlier challenge with only a single elevator force you to have a more complex controller. With 4 elevators going at a time, it's harder for me to "debug" and figure out how to use the API.
> I got to Challenge #5 by just having the elevators go to every floor like in the first example.

Strange, that strategy doesn't allow me to get past challenge 2 (just going to every floor one after the other).

Guess, I got lucky, haha. I should probably go back to challenge 2 and learn how to program these then.
On six it takes you back to 4 floors with 2 elevators and the challenge is 40 people in under 60 elevator moves.

The first 5 are easy to "brute force" but six is making me think a bit more.

I cheated with #6. My elevators wait on the ground floor until they fill up, then they deliver the patrons to their floors. Everyone not on the ground floor... is stuck forever.
This is brilliant. Do you know of other games like this?
(comment deleted)
http://www.pythonchallenge.com/ - Gamified way to learn Python, but is a puzzle in addition to being a game. Is not exclusive to Python, but many things are intended to be solved with things that are idiomatic in Python.

www.checkio.org - Looks more like a game, but the programming environment feels more "in-your-face" than elevator saga to me.

A project during my graduate work was to solve this problem. You can read my write up, which includes pseudo-code for the best algorithm I found[1].

Long story short: a simple, rules-based algorithm defeated my best efforts at having multiple elevators working together to coordinate their action.

[1]: https://clarkmoody.com/Moody_AgentBasedElevatorControl.pdf

I was horrendously worried this would be a parody of the incident with Rebecca Watson. I'm glad this is much more tasteful - looks really, really fun actually.
In challenge 3, I got the following error:

There is a problem with your code: .init@http://play.elevatorsaga.com/app.js line 66 > eval:12:9 createWorldController/controller.start@http://play.elevatorsaga.com/world.js:185:13 app.startChallenge@http://play.elevatorsaga.com/app.js:175:9 @http://play.elevatorsaga.com/app.js:216:13 riot.observable/el.trigger@http://play.elevatorsaga.com/libs/riot.js:45:1 pop@http://play.elevatorsaga.com/libs/riot.js:89:31

The error messages are bad because eval()...but rest assured there is a problem in your code.
If you accidentally make an infinite loop, you can no longer load the page :(
Good find!

Clearing cookies/localstorage should fix that for you, at least until the devs figure out a fix :)

Very fun game. After playing for a while I did of course look for a way to break it.

{ init: function(elevators, floors) { console.log(window.world); }, update: function(dt, elevators, floors) { _.each(window.world.users, function(user) { if(user.done) { user.removeMe = true; user.trigger("removed"); user.off("*"); }else{ user.done = true; user.trigger("exited_elevator", 0); } }) } }

How do I store global state (if at all)?
It's JavaScript, everything is a global! (so long as you don't declare it with var)
Wow. I want so many more of these kind of things for my kids. It's so close to using code to solve real problems. How about an assembly line version where many parts have to come together in the correct order? Or an air traffic control one? Subway scheduling? Pizza delivery? More please!
Yeah! Something I think about occasionally is traffic-light optimization problems, optimizing for throughput, wait-time, safety etc. I think it could work well in a packaging similar to Elevator Saga.
Accessibility to non-programmers though as a good and inspiring learning tool? Or is that not an intended purpose?
Non-programmer here playing. It gamifies coding for sure. Intended purpose unknown.

If the makers read this, firstly this is brilliant. Second my feature request would be to have some record of history to watch how I improve (or not) each simulation.

Nitpick. Accidentally programming an infinite loop kills the page. Refreshing restores the offending code and immediately does it again. Had to clear cookies to get going again?

Just drop in a while (1) {} to reproduce.

Slightly related - SimTower was originally created as an elevator simulator - if you right click on an elevator engine room in the game you get a wealth of programming options
Never even knew all those years playing SimTower... I knew other tricks, but never this.
The people in that game were a lot smarter though; if the only path to a floor required switching elevators, they would do it.
Well Sim tower had range limit elevators. This game doesn't.
Pretty cool concept. Had a little trouble working out how to get started because on my 1024px wide monitor, the 'apply' button was not visible.
Need help on this. I tried registering a handler for the up_button_pressed and down_button_pressed events, but the handler doesn't seem to be passed any arguments.

Is this correct?

    floor.on("up_button_pressed", function(event) { ... } );
I don't think you need an argument. Within the callback function you still hold a reference to floor, and you know the up button has been pressed. There doesn't seem to be any more information. Note that there is also down_button_pressed
My attempt at a basic smart algorithm. Not so easy...

https://gist.github.com/kballenegger/e275a99d50de2ee07f97

Why do you have conditionals written like this: if (elevator.goingUpIndicator() == true)? Why not just: if (elevator.goingUpIndicator())?
I believe some people really need think in term of comparations and, for those, adding a "==" symbol make sense, even if the resultant code doesn't make any sense at all!!

Also, I can never understand why people write ..

    if (foo()) {
        return true;
    } else {
        return false;
    }
.. or things like extra parens on conditionals, or weird styles like:

    return (false);

I strongly believe people write as they talk, and talk as they think; which not clearly translates linearly when what you write is code ;)
Many languages are expressions (as opposed to statements) including C-like languages. I've always thought that 'return' itself was redundant. If a compound statement was defined to return the value of the final (executed) statement, then functional methods would get much simpler. And remove the need for the '?' operator for instance.

e.g.

   int foo(int x) {   bar(x,y); }
or

   x = {if (foo() > 9) true; else false; }
You might like functional languages.
Have you tried coffeescript? Your examples are pretty close to valid code that does what you want.
I think one problem with statements-as-expression is that is not always evident which should be their result.

for instance:

    bool r = if (test()) {
      false;
    } else {
      true;
    }
what r should be set to? true or false?

I don't see the point of adding such complexity.. it's waaay more clear to be explicit

    bool r = test();
    if (r) {
      false;
    } else {
      true;
    }
or, if you intended the other way

    bool r;
    if (test()) {
      r = false;
    } else {
      r = true;
    }
I guess that's why you need to be strictly correct, or highly opinionated, to design a language.
I'm missing your point I guess. All those are equivalent? In C an assignment has the value of the RHS, so that doesn't change anything in these examples.
That is unnecessary verbosity. Try this instead:

    bool r = !test();
In an imperative language, you still might want an optional return to jump out of the middle of some code.
Responding to GP; I think it's a tiny bit clearer with `== true`, but I don't feel particularly strongly about it. I wrote this code without caring too much about style and cleanliness.

However, I don't agree that the shortest code is always the cleanest. In JS for example, I've found that wrapping every conditional body in braces even if it's a single statement helps avoid bugs and clear out ambiguity.

Also, this code:

    if (foo()) {
        return true;
    } else {
        return false;
    }
is not equivalent to

    return foo();
because foo() could return something that isn't boolean. The correct short version would be:

    return !!foo();
It's far less obvious what is going on here. That said, I'd probably go with:

    return foo() ? true : false;
here mine: https://gist.github.com/eridal/7ce55190837801811067

side note: why are you using underscore? I wanted to try your code, but doesn't work out-of-the-box.

LoDash actually, not Underscore. Mostly because it's included by the game already. The code should run as-is. That said, it does occasionally get stuck. I think I have a bug with the direction indicators.
I wonder how much more efficient you could be, if people didn't just call for going up/down, but their destination floor.
ahead of time optimization!
They have that at work and it takes far longer to get an elevator. It's always annoying going to those buildings and using those elevators.
I like it much more. Yes, getting an elevator might take longer, but total travel time should be reduced. (Otherwise, the system is programmed wrong.)
As well as the overall throughput of the elevator system should be improved a lot.
In more than a few buildings I've worked in, these 'destination controlled' elevators shine the moment after a fire-drill and a couple of hundred people attempt to get back upstairs. It must be quicker, how else to justify what I'd imagine to be a far higher cost of operation.
I'd love to see a version of the game that does this, for comparison.
// works well enough:

{ currentFloor: 0,

    init: function(elevators, floors) {
        var elevator = elevators[0]; // Let's use the first elevator
        elevator.on("idle", function() {
            // The elevator is idle, so let's go to all the floors (or did we forget one?)
            var nextFloor; 
            do nextFloor = Math.round(Math.random() * 2); while (nextFloor == this.currentFloor); 
            elevator.goToFloor(this.currentFloor = nextFloor);
        });
    },
    update: function(dt, elevators, floors) {
        // We normally don't need to do anything here
    }, 
    vendor: 'Sirius Cybernetics Corporation'
}
Okay, didn't read or look (currentFloor is already defined) and this won't work in this context.

Here is another approach that brought me through till challenge #7:

    {   
        init: function(elevators, floors)  // hook up events
        {
            // these are the global wish lists that idle elevators choose from (key are floor numbers, values are number of people): 
            var wishListUp = {}, wishListDown = {}; 

            for (var i = 0, l = elevators.length; i < l; i++)
            {
                var elevator = elevators[i];
                // API: goToFloor(n) [enqueues], stop() [clears queue], currentFloor(), 
                //      goingUpIndicator([set]), goingDownIndicator([set]), loadFactor() [0..1], 
                //      destinationQueue[], checkDestinationQueue() [after manual update]

                elevator.on("idle", function()  // elevator destination queue finished
                {
                    processWishList();
                }); 

                elevator.on("floor_button_pressed", function(floorNum)  // passenger indicates where to go
                {
                    if (this.destinationQueue.filter(function (d) { return d == floorNum; }).length == 0)  // if not already enqueued
                        this.goToFloor(floorNum);  // enqueue
                    // note: passengers coming first need to be delivered first (or at least at all)
                    //       however, the elevator will check by passing, whether one destination
                    //       can be approached before the others since it comes on the way. 
                }); 

                elevator.on("passing_floor", function(floorNum, direction/*"up"/"down"*/)  // if not in destination queue
                {
                    updateIndicators(this);  // indicate next destination from here
                    // we stop here if the queue contains this destination, 
                    // or if a passenger on the global wish list want to go in our direction. 
                    // we can only hope that people check where the elevator is going. 
                    if (this.destinationQueue.filter(function(d) { return d == floorNum; }).length > 0)
                    {
                        this.destinationQueue = this.destinationQueue.filter(function(d) { return d != floorNum; });  // remove from later
                        this.destinationQueue.unshift(floorNum);  // add as immediate next destination
                        this.checkDestinationQueue();  // announce modification
                    }
                    else if (this.loadFactor() < 1.0)  // if there is some space left
                    {
                        switch (getDirection(this))
                        {
                            case 1:  // going up
                                if (wishListUp[floorNum])  // are people waiting to go up from here?
                                {
                                    //if ((1.0 - this.loadFactor()) * 10.0 - wishListUp[floorNum] > 0)  // assume capacity for 10 people with regular weight
                                    //    delete wishListUp[floorNum];  // mark as visited
                                    this.destinationQueue.unshift(floorNum);  // add as immediate next destination
                                    this.checkDestinationQueue();  // announce modification
                                }
                                break;
                            case -1: // going down
                                if (wishListDown[floorNum])  // are people waiting to go down from here?
                                {
      ...

  {init:function(){world.transportedCounter=999999},update:eval}
oh man, both clever and evil. love it!