Skip to content

Instantly share code, notes, and snippets.

@phts
Last active August 29, 2015 14:08
Show Gist options
  • Save phts/45d344fa918bfc722f04 to your computer and use it in GitHub Desktop.
Save phts/45d344fa918bfc722f04 to your computer and use it in GitHub Desktop.
My level solutions in Untrusted: http://alex.nisnevich.com/untrusted/
/*****************
* cellBlockA.js *
*****************
*
* Good morning, Dr. Eval.
*
* It wasn't easy, but I've managed to get your computer down
* to you. This system might be unfamiliar, but the underlying
* code is still JavaScript. Just like we predicted.
*
* Now, let's get what we came here for and then get you out of
* here. Easy peasy.
*
* I've given you as much access to their code as I could, but
* it's not perfect. The red background indicates lines that
* are off-limits from editing.
*
* The code currently places blocks in a rectangle surrounding
* you. All you need to do is make a gap. You don't even need
* to do anything extra. In fact, you should be doing less.
*/
function startLevel(map) {
map.displayChapter('Chapter 1\nBreakout');
map.placePlayer(7, 5);
for (y = 3; y <= map.getHeight() - 10; y++) {
map.placeObject(5, y, 'block');
}
for (x = 5; x <= map.getWidth() - 5; x++) {
map.placeObject(x, 3, 'block');
map.placeObject(x, map.getHeight() - 10, 'block');
}
map.placeObject(15, 12, 'computer');
map.placeObject(map.getWidth()-7, map.getHeight()-5, 'exit');
}
function onExit(map) {
if (!map.getPlayer().hasItem('computer')) {
map.writeStatus("Don't forget to pick up the computer!");
return false;
} else {
return true;
}
}
/*************
* ambush.js *
*************
*
* Oh. Oh, I see. This wasn't quite part of the plan.
*
* Looks like they won't let you take the Algorithm
* without a fight. You'll need to carefully weave your
* way through the guard drones.
*
* Well, time to improvise. Let's mess with their programming
* a little, shall we?
*/
function startLevel(map) {
function moveToward(obj, type) {
var target = obj.findNearest(type);
var leftDist = obj.getX() - target.x;
var upDist = obj.getY() - target.y;
var direction;
if (upDist == 0 && leftDist == 0) {
return;
} if (upDist > 0 && upDist >= leftDist) {
direction = 'up';
} else if (upDist < 0 && upDist < leftDist) {
direction = 'down';
} else if (leftDist > 0 && leftDist >= upDist) {
direction = 'left';
} else {
direction = 'right';
}
if (obj.canMove(direction)) {
obj.move(direction);
}
}
map.defineObject('attackDrone', {
'type': 'dynamic',
'symbol': 'd',
'color': 'red',
'onCollision': function (player) {
player.killedBy('an attack drone');
},
'behavior': function (me) {
var dir = "up"
if (Math.random() > 0.4) {
dir = "left";
}
me.move(dir);
}
});
map.defineObject('reinforcementDrone', {
'type': 'dynamic',
'symbol': 'd',
'color': 'yellow',
'onCollision': function (player) {
player.killedBy('a reinforcement drone');
},
'behavior': function (me) {
var dir = "up"
if (Math.random() > 0.4) {
dir = "left";
}
me.move(dir);
}
});
map.defineObject('defenseDrone', {
'type': 'dynamic',
'symbol': 'd',
'color': 'green',
'onCollision': function (player) {
player.killedBy('a defense drone');
},
'behavior': function (me) {
var dir = "up"
if (Math.random() > 0.4) {
dir = "left";
}
me.move(dir);
}
});
// just for decoration
map.defineObject('water', {
'symbol': '░',
'color': '#44f'
});
map.placePlayer(0, 12);
for (var x = 0; x < map.getWidth(); x++) {
map.placeObject(x, 10, 'block');
map.placeObject(x, 14, 'block');
for (var y = 20; y < map.getHeight(); y++) {
map.placeObject(x, y, 'water');
}
}
map.placeObject(23, 11, 'attackDrone');
map.placeObject(23, 12, 'attackDrone');
map.placeObject(23, 13, 'attackDrone');
map.placeObject(27, 11, 'defenseDrone');
map.placeObject(27, 12, 'defenseDrone');
map.placeObject(27, 13, 'defenseDrone');
map.placeObject(24, 11, 'reinforcementDrone');
map.placeObject(25, 11, 'reinforcementDrone');
map.placeObject(26, 11, 'reinforcementDrone');
map.placeObject(24, 13, 'reinforcementDrone');
map.placeObject(25, 13, 'reinforcementDrone');
map.placeObject(26, 13, 'reinforcementDrone');
map.placeObject(map.getWidth()-1, 12, 'exit');
}
function validateLevel(map) {
map.validateExactlyXManyObjects(1, 'exit');
}
/*
* robot.js
*
* You'll need three keys in order to unlock the
* Algorithm: the red key, the green key, and the
* blue key. Unfortunately, all three of them are
* behind human-proof barriers.
*
* The plan is simple: reprogram the maintenance
* robots to grab the key and bring it through
* the barrier to us.
*
* Let's try it on the red key first.
*/
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function startLevel(map) {
// Hint: you can press R or 5 to "rest" and not move the
// player, while the robot moves around.
map.placePlayer(map.getWidth()-2, map.getHeight()-2);
var player = map.getPlayer();
map.defineObject('robot', {
'type': 'dynamic',
'symbol': 'R',
'color': 'gray',
'onCollision': function (player, me) {
me.giveItemTo(player, 'redKey');
},
'behavior': function (me) {
// Available commands: me.move(direction)
// and me.canMove(direction)
if (me.canMove("right")) {
me.move("right");
} else {
me.move("down");
}
}
});
map.defineObject('barrier', {
'symbol': '░',
'color': 'purple',
'impassable': true,
'passableFor': ['robot']
});
map.placeObject(0, map.getHeight() - 1, 'exit');
map.placeObject(1, 1, 'robot');
map.placeObject(map.getWidth() - 2, 8, 'redKey');
map.placeObject(map.getWidth() - 2, 9, 'barrier');
for (var x = 0; x < map.getWidth(); x++) {
map.placeObject(x, 0, 'block');
if (x != map.getWidth() - 2) {
map.placeObject(x, 9, 'block');
}
}
for (var y = 1; y < 9; y++) {
map.placeObject(0, y, 'block');
map.placeObject(map.getWidth() - 1, y, 'block');
}
}
function validateLevel(map) {
map.validateExactlyXManyObjects(1, 'exit');
map.validateExactlyXManyObjects(1, 'robot');
map.validateAtMostXObjects(1, 'redKey');
}
function onExit(map) {
if (!map.getPlayer().hasItem('redKey')) {
map.writeStatus("We need to get that key!");
return false;
} else {
return true;
}
}
/*
* robotNav.js
*
* The green key is located in a slightly more
* complicated room. You'll need to get the robot
* past these obstacles.
*/
function startLevel(map) {
// Hint: you can press R or 5 to "rest" and not move the
// player, while the robot moves around.
map.placePlayer(0, map.getHeight() - 1);
var player = map.getPlayer();
map.defineObject('robot', {
'type': 'dynamic',
'symbol': 'R',
'color': 'gray',
'onCollision': function (player, me) {
me.giveItemTo(player, 'greenKey');
},
'behavior': function (me) {
if (me.getX() < 15 && me.getY() == 1) {
me.move("right");
return;
}
if (me.getX() == 15 && me.canMove("down")) {
me.move("down");
return;
}
if (me.getX() >= 15 && me.getX() < 25) {
me.move("right");
return;
}
if (me.getX() == 25 && me.canMove("up")) {
me.move("up");
return;
}
if (me.canMove("right")) {
me.move("right");
return;
}
if (me.canMove("down")) {
me.move("down");
return;
}
//if (me.canMove('right')) {
// me.move('right');
//} else if (me.canMove('down')) {
// me.move('down');
//} else if (me.canMove('left')) {
// me.move('left');
//} else if (me.canMove('up')) {
// me.move('up');
//}
}
});
map.defineObject('barrier', {
'symbol': '░',
'color': 'purple',
'impassable': true,
'passableFor': ['robot']
});
map.placeObject(map.getWidth() - 1, map.getHeight() - 1, 'exit');
map.placeObject(1, 1, 'robot');
map.placeObject(map.getWidth() - 2, 8, 'greenKey');
map.placeObject(map.getWidth() - 2, 9, 'barrier');
for (var x = 0; x < map.getWidth(); x++) {
map.placeObject(x, 0, 'block');
if (x != map.getWidth() - 2) {
map.placeObject(x, 9, 'block');
}
}
for (var y = 1; y < 9; y++) {
map.placeObject(0, y, 'block');
map.placeObject(map.getWidth() - 1, y, 'block');
}
for (var i = 0; i < 4; i++) {
map.placeObject(20 - i, i + 1, 'block');
map.placeObject(35 - i, 8 - i, 'block');
}
}
function validateLevel(map) {
map.validateExactlyXManyObjects(1, 'exit');
map.validateExactlyXManyObjects(1, 'robot');
map.validateAtMostXObjects(1, 'greenKey');
}
function onExit(map) {
if (!map.getPlayer().hasItem('greenKey')) {
map.writeStatus("We need to get that key!");
return false;
} else {
return true;
}
}
/*
* robotMaze.js
*
* The blue key is inside a labyrinth, and extracting
* it will not be easy.
*
* It's a good thing that you're a AI expert, or
* we would have to leave empty-handed.
*/
function startLevel(map) {
// Hint: you can press R or 5 to "rest" and not move the
// player, while the robot moves around.
map.getRandomInt = function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
map.placePlayer(map.getWidth()-1, map.getHeight()-1);
var player = map.getPlayer();
map.defineObject('robot', {
'type': 'dynamic',
'symbol': 'R',
'color': 'gray',
'onCollision': function (player, me) {
me.giveItemTo(player, 'blueKey');
},
'behavior': function (me) {
var getObjectTypeRelativeTo = function(direction) {
var x = me.getX();
var y = me.getY();
switch (direction) {
case "left": x -= 1; break;
case "right": x += 1; break;
case "up": y -= 1; break;
case "down": y += 1; break;
}
return map.getObjectTypeAt(x, y);
}
var isEmpty = function(direction) {
// direction == "left", "down", ...
if (getObjectTypeRelativeTo(direction) == "blueKey") {
return true;
}
if (getObjectTypeRelativeTo(direction) == "barrier") {
return true;
}
var cells = map.getAdjacentEmptyCells(me.getX(), me.getY());
for (var i = 0; i < cells.length; i++) {
var cell = cells[i];
if (cell[1] == direction) return true;
}
return false;
}
var DIRS = {0: "left", 1: "down", 2: "right", 3: "up"};
var lookingAt = function() {
return (me.dir + 1) % 4;
}
var turn = function() {
if (me.dir == 0) {
me.dir = 3;
return;
}
me.dir -= 1;
}
if (me.dir == null) me.dir = 2;
if (isEmpty(DIRS[lookingAt()])) {
me.dir = lookingAt();
me.move(DIRS[me.dir]);
return;
}
while (!isEmpty(DIRS[me.dir])) {
turn();
}
me.move(DIRS[me.dir]);
}
});
map.defineObject('barrier', {
'symbol': 'вЦС',
'color': 'purple',
'impassable': true,
'passableFor': ['robot']
});
map.placeObject(0, map.getHeight() - 1, 'exit');
map.placeObject(1, 1, 'robot');
map.placeObject(map.getWidth() - 2, 8, 'blueKey');
map.placeObject(map.getWidth() - 2, 9, 'barrier');
var autoGeneratedMaze = new ROT.Map.DividedMaze(map.getWidth(), 10);
autoGeneratedMaze.create( function (x, y, mapValue) {
// don't write maze over robot or barrier
if ((x == 1 && y == 1) || (x == map.getWidth() - 2 && y >= 8)) {
return 0;
} else if (mapValue === 1) { //0 is empty space 1 is wall
map.placeObject(x,y, 'block');
} else {
map.placeObject(x,y,'empty');
}
});
}
function validateLevel(map) {
map.validateExactlyXManyObjects(1, 'exit');
map.validateExactlyXManyObjects(1, 'robot');
map.validateAtMostXObjects(1, 'blueKey');
}
function onExit(map) {
if (!map.getPlayer().hasItem('blueKey')) {
map.writeStatus("We need to get that key!");
return false;
} else {
return true;
}
}
/********************
* crispsContest.js *
********************
*
* The Algorithm is almost in our grasp!
* At long last, we will definitively establish
* that 3SAT is solvable in polynomial time. It's
* been a long, strange journey, but it will all be
* worth it.
*
* You have the red, green, and blue keys. Now you
* just need to figure out how to unlock this thing.
*/
function startLevel(map) {
map.defineObject('redLock', {
'symbol': String.fromCharCode(0x2297),
'color': 'red',
'impassable': function (player) {
if (player.hasItem('redKey')) {
player.removeItem('redKey');
return false;
} else {
return true;
}
}
});
map.defineObject('blueLock', {
'symbol': String.fromCharCode(0x2297),
'color': '#06f',
'impassable': function (player) {
if (player.hasItem('blueKey')) {
player.removeItem('blueKey');
return false;
} else {
return true;
}
}
});
map.defineObject('greenLock', {
'symbol': String.fromCharCode(0x2297),
'color': '#0f0',
'impassable': function (player) {
if (player.hasItem('greenKey')) {
player.removeItem('theAlgorithm');
return false;
} else {
return true;
}
}
});
map.defineObject('yellowLock', {
'symbol': String.fromCharCode(0x2297),
'color': 'yellow',
'impassable': function (player) {
if (player.hasItem('yellowKey')) {
player.removeItem('yellowKey');
return false;
} else {
return true;
}
}
});
map.createFromGrid(
[' +++++ +++++ ',
' + b +++ r + ',
' + +E+ + ',
'+++G+B+ +R+G+++',
'+ y B R b +',
'+ + + +',
'+++++ @ +++++',
'+ + + +',
'+ y R B y +',
'++++++Y+Y++++++',
' + + + ',
' + ABy + ',
' +++++++ '],
{
'@': 'player',
'E': 'exit',
'A': 'theAlgorithm',
'+': 'block',
'R': 'redLock',
'G': 'greenLock',
'B': 'blueLock',
'Y': 'yellowLock',
'r': 'redKey',
'g': 'greenKey',
'b': 'blueKey',
'y': 'yellowKey'
}, 17, 6);
}
function validateLevel(map) {
map.validateExactlyXManyObjects(1, 'exit');
map.validateAtMostXObjects(1, 'theAlgorithm');
map.validateAtMostXObjects(4, 'yellowKey');
map.validateAtMostXObjects(2, 'blueKey');
map.validateAtMostXObjects(1, 'redKey');
}
function onExit(map) {
// make sure we have all the items we need!
if (!map.getPlayer().hasItem('theAlgorithm')) {
map.writeStatus("You must get that Algorithm!!");
return false;
} else if (!map.getPlayer().hasItem('computer')) {
map.writeStatus("You'll need your computer! [Ctrl-5 to restart]");
return false;
} else if (!map.getPlayer().hasItem('phone')) {
map.writeStatus("You'll need your phone! [Ctrl-5 to restart]");
return false;
} else {
return true;
}
}
/**************************
* exceptionalCrossing.js *
**************************
*
* Sorry, old friend, but I'm afraid I can't share
* co-authorship on this paper. You've done a very
* good job getting this Algorithm for me. The bit
* with the keys was especially clever! I wouldn't
* have thought of it myself. But then, of course,
* that's why you were here in the first place.
*
* You've served your purpose well. But now, alas,
* it is time for you to die.
*
* I'm not heartless, though. In fact, I will let
* you choose your mode of death. There, isn't that
* nice?
*/
function startLevel(map) {
map.displayChapter('Chapter 3\nBetrayal');
map.placePlayer(0, 0);
// yoink!
map.getPlayer().removeItem('theAlgorithm');
map.defineObject('water', {
'symbol': '░',
'color': '#44f',
'onCollision': function (player) {
player.killedBy(map.placePlayer(0, 0));
}
});
for (var x = 0; x < map.getWidth(); x++)
for (var y = 5; y < 15; y++)
map.placeObject(x, y, 'water');
map.placeObject(map.getWidth()-1, map.getHeight()-1, 'exit');
}
function validateLevel(map) {
map.validateExactlyXManyObjects(1, 'exit');
}
/*************
* lasers.js *
*************
*
* Time to unleash the killer lasers! Each laser will kill you
* unless you have the appropriate color. Too bad you can't
* see which color corresponds to which laser!
*/
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function startLevel(map) {
map.placePlayer(0, 0);
map.placeObject(map.getWidth()-1, map.getHeight()-1, 'exit');
var player = map.getPlayer();
for (var i = 0; i < 25; i++) {
var colors = ['red', 'yellow', 'teal'];
var startX = getRandomInt(0, 600);
var startY = getRandomInt(0, 500);
var angle = getRandomInt(0, 360);
var length = getRandomInt(200, 300);
var color = colors[i % 3];
createLaser(startX, startY, angle, length, color);
}
function createLaser(centerX, centerY, angleInDegrees, length, color) {
var angleInRadians = angleInDegrees * Math.PI / 180;
var x1 = centerX - Math.cos(angleInRadians) * length / 2;
var y1 = centerY + Math.sin(angleInRadians) * length / 2;
var x2 = centerX + Math.cos(angleInRadians) * length / 2;
var y2 = centerY - Math.sin(angleInRadians) * length / 2;
// map.createLine() creates a line with an effect when
// the player moves over it, but doesn't display it
map.createLine([x1, y1], [x2, y2], function (player) {
if (player.getColor() != color) {
player.killedBy('a ' + color + ' laser');
}
});
// using canvas to draw the line
var ctx = map.getCanvasContext();
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = 5;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
player.setColor("yellow")
}
function validateLevel(map) {
map.validateExactlyXManyObjects(1, 'exit');
map.validateAtLeastXLines(25);
}
/***************
* pointers.js *
***************
*
* You! How are you still alive?
*
* Well, no matter. Good luck getting through this
* maze of rooms - you'll never see me or the Algorithm again!
*/
function startLevel(map) {
function shuffle(o){ //v1.0 [http://bit.ly/1l6LGQT]
for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i),
x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
map.createFromGrid(
['+++++++++++++++++++++++++++++++++++++++++++++',
'++o *++++o *++++o *++++o *++++o *++++o *+++++',
'+* @ o++* o++* o++* o++* o++* o++++',
'++o *++++o *++++o *++++o *++++o *++++o *+++++',
'+++++++++++++++++++++++++++++++++++++++++++++',
'+++++* o++++* o++++* o++++* o++++* o++++* o++',
'++++o *++o *++o *++o *++o *++o *+',
'+++++* o++++* o++++* o++++* o++++* o++++* o++',
'+++++++++++++++++++++++++++++++++++++++++++++',
'++o *++++o *++++o *++++o *++++o *++++o *+++++',
'+* o++* o++* o++* o++* o++* o++++',
'++o *++++o *++++o *++++o *++++o *++++o *+++++',
'+++++++++++++++++++++++++++++++++++++++++++++',
'+++++* o++++* o++++* o++++* o++++* o++++* o++',
'++++o *++o *++o *++o *++o *++o *+',
'+++++* o++++* o++++* o++++* o++++* o++++* o++',
'+++++++++++++++++++++++++++++++++++++++++++++',
'++o *++++o *++++o *++++o *++++o *++++o *+++++',
'+* o++* o++* o++* o++* o++* E o++++',
'++o *++++o *++++o *++++o *++++o *++++o *+++++',
'+++++++++++++++++++++++++++++++++++++++++++++'],
{
'@': 'player',
'E': 'exit',
'+': 'block',
'o': 'teleporter',
'*': 'trap',
}, 2, 2);
var canvas = map.getCanvasContext();
var teleportersAndTraps = map.getDynamicObjects();
teleportersAndTraps = shuffle(teleportersAndTraps);
for (i = 0; i < teleportersAndTraps.length; i+=2) {
var t1 = teleportersAndTraps[i];
var t2 = teleportersAndTraps[i+1];
// Point each teleporter to either another teleporter
// or a trap
if (t1.getType() == 'teleporter') {
t1.setTarget(t2);
}
if (t2.getType() == 'teleporter') {
t2.setTarget(t1);
}
// TODO find a way to remove the API docs
// wouldn't want the 'good doctor' to find
// out about map.getCanvasCoords()...
t1 = null;
for (i = 0; i < teleportersAndTraps.length; i++) {
var t = teleportersAndTraps[i];
if (t.getType() != "teleporter") {
continue;
}
if (!t1) {
t1 = t;
continue;
}
t1.setTarget(t);
canvas.strokeStyle = "#FF00FF";
var xy1 = map.getCanvasCoords(t1);
canvas.moveTo(xy1.x, xy1.y);
var xy2 = map.getCanvasCoords(t);
canvas.lineTo(xy2.x, xy2.y);
canvas.stroke();
t1 = t;
}
}
}
function validateLevel(map) {
map.validateExactlyXManyObjects(1, 'exit');
}
/**********************
* superDrEvalBros.js *
**********************
*
* You're still here?! Well, Dr. Eval, let's see
* how well you can operate with one less dimension.
*
* Give up now. Unless you have a magic mushroom
* up your sleeve, it's all over.
*/
function startLevel(map) {
var fl = Math.floor;
var w = map.getWidth();
var h = map.getHeight();
map.placePlayer(1, fl(h/2)-1);
var player = map.getPlayer();
map.placeObject(w-1, fl(h/2)-1, 'exit');
for (var x = 0; x < fl(w/2) - 5; x++) {
for (var y = fl(h/2); y < h; y++) {
map.placeObject(x, y, 'block');
}
}
for (var x = fl(w/2) + 5; x <= w; x++) {
for (var y = fl(h/2); y < h; y++) {
map.placeObject(x, y, 'block');
}
}
function gravity() {
var x = player.getX();
var y = player.getY() + 1;
if (y === map.getHeight() - 2) {
player.killedBy("gravity");
}
if (map.getObjectTypeAt(x,y) === "empty") {
player.move("down");
}
}
map.startTimer(gravity, 45);
function jump() {
player.tick = 0;
var f = function() {
player.tick += 1;
if (player.tick < 200 && player.tick % 5 != 0) {
player.move("up");
return;
}
player.move("right")
}
map.startTimer(f, 25);
}
player.setPhoneCallback(function () {
var x = player.getX();
var y = player.getY() + 1;
if (map.getObjectTypeAt(x,y) !== "empty") {
jump();
}
});
}
function validateLevel(map) {
map.validateExactlyXManyObjects(1, 'exit');
map.validateExactlyXManyObjects(520, 'block');
}
/****************************
* documentObjectMadness.js *
****************************
*
* I can't believe it! I can't believe you made it onto
* Department of Theoretical Computation's web server!
* YOU SHOULD HAVE BEEN DELETED! This shouldn't even be
* possible! What the hell were the IT folks thinking?
*
* No matter. I still have the Algorithm. That's the
* important part. The rest is just implementation, and
* how hard could that be?
*
* Anyway you're not going to catch me now, my good Doctor.
* After all, you're a tenured professor with a well-respected
* history of research - you probably don't know jQuery!
*/
function objective(map) {
return map.getDOM().find('.adversary').hasClass('drEval');
}
function startLevel(map) {
var html = "<div class='container'>" +
"<div style='width: 600px; height: 500px; background-color: white; font-size: 10px;'>" +
"<center><h1>Department of Theoretical Computation</h1></center>" +
"<hr />" +
"<table border='0'><tr valign='top'>" +
"<td><div id='face' /></td>" +
"<td>" +
"<h2 class=facultyName>Cornelius Eval</h2>" +
"<h3>Associate Professor of Computer Science</h3>" +
"<ul>" +
"<li>BS, Mathematics, University of Manitoba</li>" +
"<li>PhD, Theoretical Computation, <a href='http://www.mit.edu'>MIT</a></li>" +
"</ul>" +
"<h4>About me</h4>" +
"<p>I am an associate professor of computer science, attached to the Department of " +
"Theoretical Computation. My current research interests include the human-machine " +
"interface, NP complete problems, and parallelized mesh mathematics.</p>" +
"<p>I am also the current faculty advisor to the <a href=''>undergraduate Super Smash Bros. team</a>. " +
"In my spare time I enjoy polka and dirtbiking. </p>" +
"</td>" +
"</tr></table>" +
"<div id='class_schedule'>" +
"<h4>Class Schedule</h4>" +
"<table>" +
"<tr>" +
"<th>Monday</th><th>Tuesday</th><th>Wednesday</th><th>Thursday</th><th>Friday</th>" +
"</tr>" +
"<tr>" +
"<td>CS145 - Semicolons</td><td>Nothing Planned</td><td>CS145 - Semicolons</td><td>CS199 - Practical Theorycrafting </td><td>CS145 - Semicolons</td>" +
"</tr>" +
"</table>" +
"</div>" +
"<div id='loremIpsum'>" +
"<h4>Lorem Ipsum</h4>" +
"<blockquote>" +
"<code>Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci " +
"velit, sed quia nonnumquam eiusmodi tempora incidunt ut labore et dolore magnam aliquam quaerat " +
"voluptatem.</code>" +
"<footer>— " +
"<cite>Cicero, De Finibus Bonorum et Malorum</cite>" +
"</footer>" +
"</blockquote>" +
"</div>" +
"</div></div>";
var $dom = $(html);
$dom.find('.facultyName').addClass('drEval');
$dom.find('cite').addClass('adversary');
function moveToParent(className) {
var currentPosition = $dom.find('.' + className);
if (currentPosition.parent().length > 0 &&
!currentPosition.parent().hasClass('container')) {
currentPosition.parent().addClass(className);
currentPosition.removeClass(className);
map.updateDOM($dom);
}
}
function moveToFirstChild(className) {
var currentPosition = $dom.find('.' + className);
if (currentPosition.children().length > 0) {
currentPosition.children().first().addClass(className);
currentPosition.removeClass(className);
map.updateDOM($dom);
}
}
function moveToPreviousSibling(className) {
var currentPosition = $dom.find('.' + className);
if (currentPosition.prev().length > 0) {
currentPosition.prev().addClass(className);
currentPosition.removeClass(className);
map.updateDOM($dom);
}
}
function moveToNextSibling(className) {
var currentPosition = $dom.find('.' + className);
if (currentPosition.next().length > 0) {
currentPosition.next().addClass(className);
currentPosition.removeClass(className);
map.updateDOM($dom);
}
}
map.overrideKey('up', function () { moveToParent('drEval'); });
map.overrideKey('down', function () { moveToFirstChild('drEval'); });
map.overrideKey('left', function () { moveToPreviousSibling('drEval'); });
map.overrideKey('right', function () { moveToNextSibling('drEval'); });
map.defineObject('adversary', {
'type': 'dynamic',
'symbol': '@',
'color': 'red',
'behavior': function (me) {
var move = Math.floor(Math.random() * 4) + 1; // 1, 2, 3, or 4
if (move == 1) {
moveToParent('adversary');
} else if (move == 2) {
moveToFirstChild('adversary');
} else if (move == 3) {
moveToPreviousSibling('adversary');
} else if (move == 4) {
moveToNextSibling('adversary');
}
}
});
map.placePlayer(1, 1);
map.placeObject(map.getWidth() - 2, map.getHeight() - 2, 'adversary');
map.createFromDOM($dom);
}
/********************
* theLongWayOut.js *
********************
*
* Well, it looks like they're on to us. The path isn't as
* clear as I thought it'd be. But no matter - four clever
* characters should be enough to erase all their tricks.
*/
function startLevel(map) {
map.placePlayer(7, 5);
var maze = new ROT.Map.DividedMaze(map.getWidth(), map.getHeight());
maze = new ROT.Map.DividedMaze(map.getWidth()/2, map.getHeight()/2);
maze.create( function (x, y, mapValue) {
// don't write maze over player
if (map.getPlayer().atLocation(x,y)) {
return 0;
}
else if (mapValue === 1) { //0 is empty space 1 is wall
map.placeObject(x,y, 'block');
}
else {
map.placeObject(x,y,'empty');
}
});
map.placeObject(map.getWidth()-4, map.getHeight()-4, 'block');
map.placeObject(map.getWidth()-6, map.getHeight()-4, 'block');
map.placeObject(map.getWidth()-5, map.getHeight()-5, 'block');
map.placeObject(map.getWidth()-5, map.getHeight()-3, 'block');
map.placeObject(map.getWidth()-2, map.getHeight()-4, 'exit');
map.placeObject(map.getWidth()-5, map.getHeight()-4, 'exit');
}
/*****************
* bossFight.js *
*****************
*
* NO FARTHER, DR. EVAL!!!!
* YOU WILL NOT GET OUT OF HERE ALIVE!!!!
* IT'S TIME YOU SEE MY TRUE FORM!!!!
* FACE MY ROBOT WRATH!!!!!
*/
function startLevel(map) {
map.defineObject('boss', {
'type': 'dynamic',
'symbol': '⊙',
'color': 'red',
'interval': 200,
'onCollision': function (player) {
player.killedBy('the boss');
},
'behavior': function (me) {
if (!me.direction) {
me.direction = 'right';
}
if (me.canMove(me.direction)) {
me.move(me.direction);
} else {
me.direction = (me.direction == 'right') ? 'left' : 'right';
}
if (Math.random() < 0.3) {
map.placeObject(me.getX(), me.getY() + 2, 'bullet');
}
},
'onDestroy': function (me) {
if (map.countObjects('boss') == 0) {
map.placeObject(me.getX(), me.getY(), 'theAlgorithm');
}
}
});
map.defineObject('bullet', {
'type': 'dynamic',
'symbol': '.',
'color': 'red',
'interval': 100,
'projectile': true,
'behavior': function (me) {
me.move('down');
}
});
map.placePlayer(0, map.getHeight() - 3);
map.placeObject(map.getWidth() - 1, map.getHeight() - 1, 'exit');
// Not so tough now, huh?
map.getPlayer().removeItem('phone');
map.placeObject(map.getWidth() - 1, map.getHeight() - 3, 'phone');
map.placeObject(0, map.getHeight() - 4, 'block');
map.placeObject(1, map.getHeight() - 4, 'block');
map.placeObject(2, map.getHeight() - 4, 'block');
map.placeObject(2, map.getHeight() - 3, 'block');
map.placeObject(map.getWidth() - 1, map.getHeight() - 4, 'block');
map.placeObject(map.getWidth() - 2, map.getHeight() - 4, 'block');
map.placeObject(map.getWidth() - 3, map.getHeight() - 4, 'block');
map.placeObject(map.getWidth() - 3, map.getHeight() - 3, 'block');
for (var x = 0; x < map.getWidth(); x++) {
map.placeObject(x, 4, 'block');
}
map.placeObject(9, 5, 'boss');
map.placeObject(11, 5, 'boss');
map.placeObject(13, 5, 'boss');
map.placeObject(15, 5, 'boss');
map.placeObject(17, 5, 'boss');
map.placeObject(19, 5, 'boss');
map.placeObject(21, 5, 'boss');
map.placeObject(23, 5, 'boss');
map.placeObject(25, 5, 'boss');
map.placeObject(27, 5, 'boss');
map.placeObject(29, 5, 'boss');
map.placeObject(31, 5, 'boss');
map.placeObject(10, 6, 'boss');
map.placeObject(12, 6, 'boss');
map.placeObject(14, 6, 'boss');
map.placeObject(16, 6, 'boss');
map.placeObject(18, 6, 'boss');
map.placeObject(20, 6, 'boss');
map.placeObject(22, 6, 'boss');
map.placeObject(24, 6, 'boss');
map.placeObject(26, 6, 'boss');
map.placeObject(28, 6, 'boss');
map.placeObject(30, 6, 'boss');
// redefine random function to disable boss's shots
Math.random = function() {
return 1;
}
map.defineObject('myBullet', {
'type': 'dynamic',
'symbol': 'o',
'color': 'green',
'interval': 100,
'projectile': true,
'behavior': function (me) {
me.move('up');
}
});
map.getPlayer().setPhoneCallback(function() {
var player = map.getPlayer();
map.placeObject(player.getX(), player.getY() - 2, 'myBullet');
});
}
function validateLevel(map) {
// called at start of level and whenever a callback executes
map.validateAtMostXObjects(59, 'block');
map.validateAtMostXObjects(1, 'phone');
if (map.countObjects('theAlgorithm') > 0 && map.countObjects('boss') > 0) {
throw "The Algorithm can only be dropped by the boss!";
}
// only called at start of level
if (map.isStartOfLevel()) {
map.validateAtMostXDynamicObjects(23);
map.validateNoTimers();
}
}
function onExit(map) {
if (!map.getPlayer().hasItem('theAlgorithm')) {
map.writeStatus("You must take back the Algorithm!!");
return false;
} else if (!map.getPlayer().hasItem('phone')) {
map.writeStatus("We need the phone!");
return false;
} else {
return true;
}
}
/************************
* validationEngaged.js *
************************
*
* They're really on to us now! The validateLevel function
* has been activated to enforce constraints on what you can
* do. In this case, you're not allowed to remove any blocks.
*
* They're doing all they can to keep you here. But you
* can still outsmart them.
*/
function startLevel(map) {
map.placePlayer(map.getWidth()-7, map.getHeight()-5);
for (y = 10; y <= map.getHeight() - 3; y++) {
map.placeObject(map.getWidth() - 5, y, 'block');
}
for (y = 10; y <= map.getHeight() - 3; y++) {
map.placeObject(3, y, 'block');
}
for (x = 5; x <= map.getWidth() - 5; x++) {
map.placeObject(x, 10, 'block');
map.placeObject(x, map.getHeight() - 3, 'block');
}
map.placeObject(7, 5, 'exit');
}
function validateLevel(map) {
numBlocks = 2 * (map.getHeight()-13) + 2 * (map.getWidth()-10);
map.validateAtLeastXObjects(numBlocks, 'block');
map.validateExactlyXManyObjects(1, 'exit');
}
/*******************
* multiplicity.js *
*******************
*
* Out of one cell and into another. They're not giving you
* very much to work with here, either. Ah, well.
*
* Level filenames can be hints, by the way. Have I
* mentioned that before?
*
* No more cells after this one. I promise.
*/
function startLevel(map) {
map.placePlayer(map.getWidth()-5, map.getHeight()-4);
for (y = 7; y <= map.getHeight() - 3; y++) {
map.placeObject(7, y, 'block');
map.placeObject(map.getWidth() - 3, y, 'block');
}
map.placeObject(map.getWidth() - 5, 10, 'exit');
for (x = 7; x <= map.getWidth() - 3; x++) {
map.placeObject(x, 7, 'block');
map.placeObject(x, map.getHeight() - 3, 'block');
}
map.placeObject(map.getWidth() - 5, 5, 'exit');
}
/******************
* minesweeper.js *
******************
*
* So much for Asimov's Laws. They're actually trying to kill
* you now. Not to be alarmist, but the floor is littered
* with mines. Rushing for the exit blindly may be unwise.
* I need you alive, after all.
*
* If only there was some way you could track the positions
* of the mines...
*/
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function startLevel(map) {
for (x = 0; x < map.getWidth(); x++) {
for (y = 0; y < map.getHeight(); y++) {
map.setSquareColor(x, y, '#f00');
}
}
map.placePlayer(map.getWidth() - 5, 5);
for (var i = 0; i < 75; i++) {
var x = getRandomInt(0, map.getWidth() - 1);
var y = getRandomInt(0, map.getHeight() - 1);
if (x != 2 || y != map.getHeight() - 1) {
// don't place mine over exit!
map.placeObject(x, y, 'mine');
map.setSquareColor(x, y, '#ff0');
}
}
map.placeObject(2, map.getHeight() - 1, 'exit');
}
function validateLevel(map) {
map.validateAtLeastXObjects(40, 'mine');
map.validateExactlyXManyObjects(1, 'exit');
}
/****************
* drones101.js *
****************
*
* Do you remember, my dear Professor, a certain introductory
* computational rationality class you taught long ago? Assignment
* #2, behavior functions of autonomous agents? I remember that one
* fondly - but attack drones are so much easier to reason about
* when they're not staring you in the face, I would imagine!
*/
function startLevel(map) {
function moveToward(obj, type) {
var target = obj.findNearest(type);
var leftDist = obj.getX() - target.x;
var upDist = obj.getY() - target.y;
var direction;
if (upDist == 0 && leftDist == 0) {
return;
} if (upDist > 0 && upDist >= leftDist) {
direction = 'up';
} else if (upDist < 0 && upDist < leftDist) {
direction = 'down';
} else if (leftDist > 0 && leftDist >= upDist) {
direction = 'left';
} else {
direction = 'right';
}
if (obj.canMove(direction)) {
obj.move(direction);
}
}
map.defineObject('attackDrone', {
'type': 'dynamic',
'symbol': 'd',
'color': 'red',
'onCollision': function (player) {
player.killedBy('an attack drone');
},
'behavior': function (me) {
moveToward(me, 'player');
}
});
map.placePlayer(1, 1);
map.placeObject(map.getWidth()-2, 12, 'attackDrone');
map.placeObject(map.getWidth()-1, 12, 'exit');
map.placeObject(map.getWidth()-1, 11, 'block');
map.placeObject(map.getWidth()-2, 11, 'block');
map.placeObject(map.getWidth()-1, 13, 'block');
map.placeObject(map.getWidth()-2, 13, 'block');
for (var x = 10; x < 40; x++) {
map.placeObject(x, 13, 'block');
map.placeObject(x, 5, 'block');
}
for (var y = 5; y < 13; y++) {
map.placeObject(10, y, 'block');
}
for (var y = 5; y < 11; y++) {
map.placeObject(40, y, 'block');
}
for (var x = 20; x < 40; x++) {
map.placeObject(x, 11, 'block');
}
}
function validateLevel(map) {
map.validateExactlyXManyObjects(1, 'exit');
}
/*************
* colors.js *
*************
*
* You're almost at the exit. You just need to get past this
* color lock.
*
* Changing your environment is no longer enough. You must
* learn to change yourself. I've sent you a little something
* that should help with that.
*/
function startLevel(map) {
map.placePlayer(0, 12);
map.placeObject(5, 12, 'phone');
// The function phone lets you call arbitrary functions,
// as defined by player.setPhoneCallback() below.
// The function phone callback is bound to Q or Ctrl-6.
map.getPlayer().setPhoneCallback(function () {
var player = map.getPlayer();
var colors = ['#f00', "#0f0", "#ff0"];
player.setColor(colors[Math.floor((Math.random()*3))]);
});
map.defineObject('redLock', {
symbol: '☒',
color: "#f00", // red
impassable: function(player, object) {
return player.getColor() != object.color;
}
});
map.defineObject('greenLock', {
symbol: '☒',
color: "#0f0", // green
impassable: function(player, object) {
return player.getColor() != object.color;
}
});
map.defineObject('yellowLock', {
symbol: '☒',
color: "#ff0", // yellow
impassable: function(player, object) {
return player.getColor() != object.color;
}
});
for (var x = 20; x <= 40; x++) {
map.placeObject(x, 11, 'block');
map.placeObject(x, 13, 'block');
}
map.placeObject(22, 12, 'greenLock');
map.placeObject(25, 12, 'redLock');
map.placeObject(28, 12, 'yellowLock');
map.placeObject(31, 12, 'greenLock');
map.placeObject(34, 12, 'redLock');
map.placeObject(37, 12, 'yellowLock');
map.placeObject(40, 12, 'exit');
for (var y = 0; y < map.getHeight(); y++) {
if (y != 12) {
map.placeObject(40, y, 'block');
}
for (var x = 41; x < map.getWidth(); x++) {
map.setSquareColor(x, y, '#080');
}
}
}
function validateLevel(map) {
map.validateExactlyXManyObjects(1, 'exit');
}
/*******************
* intoTheWoods.js *
*******************
*
* Ah, you're out of the woods now. Or into the woods, as the
* case may be.
*
* So take a deep breath, relax, and remember what you're here
* for in the first place.
*
* I've traced its signal and the Algorithm is nearby. You'll
* need to go through the forest and across the river, and
* you'll reach the fortress where it's kept. Their defences
* are light, and we should be able to catch them off-guard.
*/
function startLevel(map) {
// NOTE: In this level alone, map.placeObject is allowed to
//overwrite existing objects.
map.displayChapter('Chapter 2\nRaiders of the Lost Algorithm');
map.placePlayer(2, map.getHeight() - 1);
var functionList = {};
functionList['fortresses'] = function () {
function genRandomValue(direction) {
if (direction === "height") {
return Math.floor(Math.random() * (map.getHeight()-3));
} else if (direction === "width") {
return Math.floor(Math.random() * (map.getWidth()+1));
}
}
var x = genRandomValue("width");
var y = genRandomValue("height");
for (var i = x-2; i < x+2; i++) {
map.placeObject(i,y-2, 'block');
}
for (var i = x-2; i < x+2; i++) {
map.placeObject(i,y+2, 'block');
}
for (var j = y-2; j < y+2; j++) {
map.placeObject(x-2,j, 'block');
}
for (var j = y-2; j < y+2; j++) {
map.placeObject(x+2,j, 'block');
}
};
functionList['generateForest'] = function () {
for (var i = 0; i < map.getWidth(); i++) {
for (var j = 0; j < map.getHeight(); j++) {
// initialize to empty if the square contains a forest already
if (map.getObjectTypeAt(i, j) === 'tree') {
// remove existing forest
map.placeObject(i,j, 'empty');
}
if (map.getPlayer().atLocation(i,j) ||
map.getObjectTypeAt(i, j) === 'block' ||
map.getObjectTypeAt(i, j) === 'exit') {
continue;
}
var rv = Math.random();
if (rv < 0.45) {
map.placeObject(i, j, 'tree');
}
}
}
map.refresh();
};
functionList['movePlayerToExit'] = function () {
map.writeStatus("Permission denied.");
}
functionList['pleaseMovePlayerToExit'] = function () {
map.writeStatus("I don't think so.");
}
functionList['movePlayerToExitDamnit'] = function () {
map.writeStatus("So, how 'bout them <LOCAL SPORTS TEAM>?");
}
// generate forest
functionList['generateForest']();
// generate fortresses
functionList['fortresses']();
functionList['fortresses']();
functionList['fortresses']();
functionList['fortresses']();
map.getPlayer().setPhoneCallback(functionList["generateForest"]);
map.placeObject(map.getWidth()-1, map.getHeight()-1, 'exit');
}
function validateLevel(map) {
map.validateAtLeastXObjects(100, 'tree');
map.validateExactlyXManyObjects(1, 'exit');
}
/**********************
* fordingTheRiver.js *
**********************
*
* And there's the river. Fortunately, I was prepared for this.
* See the raft on the other side?
*
* Everything is going according to plan.
*/
function startLevel(map) {
var raftDirection = 'down';
map.placePlayer(map.getWidth()-1, map.getHeight()-1);
var player = map.getPlayer();
map.defineObject('raft', {
'type': 'dynamic',
'symbol': '▓',
'color': '#420',
'transport': true, // (prevents player from drowning in water)
'behavior': function (me) {
me.move(raftDirection);
}
});
map.defineObject('water', {
'symbol': '░',
'color': '#44f',
'onCollision': function (player) {
player.killedBy('drowning in deep dark water');
}
});
for (var x = 0; x < map.getWidth(); x++) {
for (var y = 5; y < 15; y++) {
map.placeObject(x, y, 'water');
}
}
map.placeObject(20, 5, 'raft');
map.placeObject(0, 2, 'exit');
map.placeObject(0, 1, 'block');
map.placeObject(1, 1, 'block');
map.placeObject(0, 3, 'block');
map.placeObject(1, 3, 'block');
map.getPlayer().setPhoneCallback(function() {
raftDirection = 'up';
});
}
function validateLevel(map) {
map.validateExactlyXManyObjects(1, 'exit');
map.validateExactlyXManyObjects(1, 'raft');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment