www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

jeu.js (12853B)


      1 function loadSprites(fileNames, callback) {
      2 	var sprites = {};
      3 	var loaders = [];
      4 	
      5 	$.each(fileNames, function(name, src) {
      6 		var deferred = $.Deferred();
      7 		var	img = new Image();
      8 		img.onload = function() { deferred.resolve(); };
      9 		img.src = src;
     10 		sprites[name] = img;
     11 		loaders.push(deferred.promise());
     12 	});
     13 	
     14 	$.when.apply($, loaders).done(function() { callback(sprites); });
     15 }
     16 
     17 // Type System
     18 
     19 var Type = {
     20 	// Primitive types
     21 	Void: function() { return { primitive: "Void" }; },
     22 	Unit: function() { return { primitive: "Unit" }; },
     23 	Int: function() { return { primitive: "Int" }; },
     24 	Struct: function(fields) { // Type.Struct({ a: Type.Int(), b: Type.Unit(), c: ... })
     25 		return {
     26 			primitive: "Struct",
     27 			fields: fields,
     28 		};
     29 	},
     30 	Function: function(parameterTypes, returnType) { // Type.Function([Type.Int(), Type.Int()], Type.Int())
     31 		return {
     32 			primitive: "Function",
     33 			parameterTypes: parameterTypes,
     34 			returnType: returnType,
     35 		};
     36 	},
     37 	Either: function(taggedUnion) { // Type.Either({ something: Type.SomeType(...), nothing: Type.Unit() });
     38 		return {
     39 			primitive: "Either",
     40 			taggedUnion: taggedUnion,
     41 		};
     42 	},
     43 	Map: function(fromName, fromType, toName, toType) {
     44 		return {
     45 			primitive: "Map",
     46 			fromName: fromName,
     47 			fromType: fromType,
     48 			toName: toName,
     49 			toType: toType,
     50 		};
     51 	},
     52 	// User-defined types
     53 	Maybe: function(type) {
     54 		return Type.Either({
     55 			something: type,
     56 			nothing: Type.Unit()
     57 		});
     58 	},
     59 	Enum: function(symbols) {
     60 		var assoc = {}
     61 		for (var i = 0; i < symbols.length; i++) {
     62 			assoc[i] = Type.Unit();
     63 		}
     64 		return Pattern.Either(assoc);
     65 	},
     66 	Boolean: function() {
     67 		return Type.Enum([
     68 			'true',
     69 			'false'
     70 		]);
     71 	}
     72 };
     73 
     74 var Pattern = {
     75 	Any: function() { return function(value) { return true; }; },
     76 	Void: function() { return function(value) { return false; }; }, // Void can never have a value.
     77 	Predicate: function(predicate) { return predicate; },
     78 	Unit: function() { return function(value) { return value.primitive == "Unit"; }; },
     79 	AnyInt: function() { return function(value) { return value.primitive == "Int"; }; },
     80 	Int: function(intValue) { return function(value) { return value.primitive == "Int" && value.value == intValue; }; },
     81 	Struct: function(fields) { // Pattern.Struct({ a: Pattern.Int(), b: Pattern.Unit(), c: ... })
     82 		return function(value) {
     83 			if (value.primitive != "Struct") { return false; }
     84 			for (f in fields) { if (!fields[f](value.fields[f])) { return false; } }
     85 			for (f in value.fields) { if (!fields[f]) { return false; } } // Check `value` doesn't have extra fields.
     86 			return true;
     87 		};
     88 	},
     89 	Function: function(parameterTypes, returnType) { // Pattern.Function([Pattern.Int(), Pattern.Int()], Pattern.Int())
     90 		return function(value) {
     91 			if (value.primitive != "Function") { return false; }
     92 			if (!returnType(value.returnType)) { return false; }
     93 			if (parameterTypes.length != value.parameterTypes.length) { return false; }
     94 			for (var i = 0; i < parameterTypes; i++) {
     95 				if (!parameterTypes[i](value.parameterTypes[i])) { return false; }
     96 			}
     97 			return true;
     98 		};
     99 	},
    100 	Either: function(taggedUnion) { // Pattern.Either({ something: Pattern.SomeType(...), nothing: Pattern.Unit()});
    101 		return function(value) {
    102 			if (value.primitive != "Either") { return false; }
    103 			if (!taggedUnion[value.tag]) { return false; }
    104 			return taggedUnion[value.tag](value.value);
    105 		};
    106 	},
    107 	OneOf: function(untaggedUnion) {
    108 		return function(value) {
    109 			for (i = 0; i < untaggedUnion.length; i++) {
    110 				if (untaggedUnion[i](value)) {
    111 					return true;
    112 				}
    113 			}
    114 			return false;
    115 		}
    116 	},
    117 	// User-defined patterns
    118 	Maybe: function(pattern) {
    119 		return Pattern.OneOf([
    120 			pattern,
    121 			Pattern.Unit()
    122 		]);
    123 	},
    124 };
    125 
    126 var Value = {
    127 	// Void can't ever have a value.
    128 	Unit: function() { return { primitive: "Unit" }; },
    129 	Int: function(value) { return { primitive: "Int", value: value }; },
    130 	Struct: function(fields) { // Value.Struct({ a: Value.Int(42), b: Value.Unit(), c: ... })
    131 		return {
    132 			primitive: "Struct",
    133 			fields: fields,
    134 		};
    135 	},
    136 	Function: function(parameterTypes, returnType, body) { // Value.Function([Type.Int(), Type.Int()], Type.Int(), body)
    137 		return {
    138 			primitive: "Function",
    139 			parameterTypes: parameterTypes,
    140 			returnType: returnType,
    141 			body: body,
    142 		};
    143 	},
    144 	Either: function(tag, value) { // Value.Either("something", Value.SomeType(...));
    145 		return {
    146 			primitive: "Either",
    147 			tag: tag,
    148 			value: value,
    149 		};
    150 	},
    151 };
    152 
    153 // Test
    154 (function() {
    155 	var maybeCellType = Type.Maybe(Type.Int());
    156 	var maybeCellPattern = Pattern.Maybe(Pattern.AnyInt());
    157 	var cellValue = Value.Int(42);
    158 	var eitherCellPattern = Pattern.Either({ cell: Pattern.AnyInt(), foobar: Pattern.AnyInt() });
    159 	var eitherCellValue = Value.Either("cell", cellValue);
    160 	if (console) {
    161 		console.log(true, maybeCellPattern(cellValue));
    162 		console.log(false, maybeCellPattern(eitherCellValue));
    163 		console.log(true, eitherCellPattern(eitherCellValue));
    164 	}
    165 })();
    166 
    167 // DONE :
    168 // Type system: Types, Pattern matching and Values
    169 // Grid cells with {floor: new Floor(), actor: new Actor()}
    170 //   where Floor has 4 "push" input/output directions, 4 input directions and 4 output directions.
    171 // 
    172 // TODO :
    173 // Type system:
    174 //   creating patterns from types,
    175 //   verifying if a value is of the given type,
    176 //   verifying if a pattern is matches against values of the given type.
    177 // Type system:
    178 //   Maybe, Either and OrElse have slightly different meanings in types/patterns/values.
    179 //   Display types, values and patterns.
    180 // Grid pattern matching:
    181 //   using relative up/right/down/left grid positions, and absolute coordinates
    182 //   Then, using the i/o paths that the floor tiles construct
    183 // TODO: the i/o paths we currently have do not allow for teleports.
    184 
    185 var GameType = {};
    186 
    187 GameType.Direction = Type.Enum([
    188 	'up',
    189 	'down',
    190 	'left',
    191 	'right',
    192 ]);
    193 
    194 GameType.Position = Type.Struct({
    195 	x: Type.Int(),
    196 	y: Type.Int(),
    197 });
    198 
    199 GameType.FloorTile = Type.Enum([
    200 	'floor',
    201 	'grass',
    202 	'hole',
    203 	'sand',
    204 	'wall',
    205 	'filledhole',
    206 ]);
    207 
    208 GameType.Floor = Type.Struct({
    209 	tile: GameType.FloorTile,
    210 	push: Type.Map('in', GameType.Direction, 'out', GameType.Direction),
    211 	allowedIn: Type.Map('in', GameType.Direction, 'allowed', Type.Boolean()),
    212 	allowedOut: Type.Map('out', GameType.Direction, 'allowed', Type.Boolean()),
    213 });
    214 
    215 GameType.TriggerTile = Type.Enum([
    216 	'end',
    217 ]);
    218 
    219 GameType.ActorTile = Type.Enum([
    220 	'player',
    221 	'block',
    222 ]);
    223 
    224 GameType.Cell = Type.Struct({
    225 	floor: GameType.Floor,
    226 	trigger: Type.Maybe(GameType.Trigger),
    227 	actor: Type.Maybe(GameType.Actor),
    228 });
    229 
    230 GamePattern = {};
    231 
    232 GamePattern.AnchoredCell = function(cellPattern, positionPattern) {
    233 };
    234 
    235 GamePattern.Subgrid = function(subgrid) { // subgrid = Array(Array(Either(GamePattern.Cell, GamePattern.AnchoredCell)))
    236 	
    237 };
    238 
    239 function Position(x, y) {
    240 	this.x = x;
    241 	this.y = y;
    242 	this.offsetX = function(offsetX) { return new Position(this.x + offsetX, this.y); }
    243 	this.offsetY = function(offsetY) { return new Position(this.x, this.y + offsetY); }
    244 	this.offset = function(a, b) {
    245 		if (arguments.length == 1) { // Position
    246 			return new Position(this.x + a.x, this.y + a.y);
    247 		} else { // x, y
    248 			return new Position(this.x + a, this.y + b);
    249 		}
    250 	}
    251 	// this.toString = function() { return 'Position(' + x + ', ' + y + ')'; };
    252 }
    253 
    254 function Rule(event, condition, action) {
    255 	this.event = event;
    256 	this.condition = condition;
    257 	this.action = action;
    258 }
    259 
    260 function Grid(width, height, initializer) {
    261 	for (var x = 0; x < width; x++) {
    262 		this[x] = [];
    263 		for (var y = 0; y < height; y++) {
    264 			this[x][y] = initializer(x, y);
    265 		}
    266 	}
    267 
    268 	this.width = width;
    269 	this.height = height;
    270 
    271 	this.get = function(a, b) {
    272 		if (b) {
    273 			return this[x][y]; // x, y
    274 		} else {
    275 			return this[a.x][a.y]; // Position
    276 		}
    277 	}
    278 
    279 	// toString()
    280 	var that = this;
    281 	this.toString = function() {
    282 		str='[';
    283 		for (var y = 0; y < that.height; y++) {
    284 			str += '[';
    285 			for (var x = 0; x < that.width; x++) {
    286 				str += '[' + that[x][y].join(',') + ']';
    287 			}
    288 			str += '],\n';
    289 		}
    290 		str += ']';
    291 		return str;
    292 	};
    293 }
    294 
    295 function Game(sprites) {
    296 	var game = this;
    297 	this.grid = new Grid(38, 25, function(x, y) { return ['h']; });
    298 	this.player = new Position(0, 0);
    299 	this.events = {
    300 		// TODO : make rules instead.
    301 		left:  function() { game.player.x--; },
    302 		up:    function() { game.player.y--; },
    303 		right: function() {
    304 			var current = game.grid.get(game.player);
    305 			var next1 = game.grid.get(game.player.offsetX(1));
    306 			var next2 = game.grid.get(game.player.offsetX(2));
    307 			for (var r = 0; r < game.rules.length; r++) {
    308 				if (game.rules[r].condition(game, current, next1, next2)) {
    309 					console.log("rule", r);
    310 					game.rules[r].action(game, current, next1, next2);
    311 					break;
    312 				}
    313 			}
    314 		},
    315 		down:  function() { game.player.y++; },
    316 	}
    317 	this.rules = [
    318 		// TODO : find a non-awkward way to express rules.
    319 		new Rule('moveToEnd', function(game, current, next1, next2) {
    320 			console.log(next1[1]);
    321 			return (current[1] == 'p') // [?,p]
    322 				&& (next1 != null) && (next1[1] == 'e'); // [e,?]
    323 		}, function(game, current, next1, next2) {
    324 			//game.player.position = next1.position;
    325 			var p = current[1];
    326 			var e = next1[1];
    327 			current.splice(1, 1);// delete starting at 1 a single (1) element;
    328 			next1[1] = p;
    329 			game.player = game.player.offsetX(1); // HACK!
    330 			alert("you win!");
    331 		}),
    332 
    333 		new Rule('pushInHole', function(game, current, next1, next2) {
    334 			return (current[1] == 'p') // [?,p]
    335 				&& (next1 != null) && (next1[1] == 'b') // [?,b]
    336 				&& (next2 != null) && (next2[0] == 'h'); // [h,?]
    337 		}, function(game, current, next1, next2) {
    338 			//game.player.position = next1.position;
    339 			var p = current[1];
    340 			var b = next1[1];
    341 			current.splice(1, 1);// delete starting at 1 a single (1) element;
    342 			next1[1] = p;
    343 			next2[0] = b;
    344 			game.player = game.player.offsetX(1); // HACK!
    345 		}),
    346 
    347 		new Rule('push', function(game, current, next1, next2) {
    348 			return (current[1] == 'p') // [?,p]
    349 				&& (next1 != null) && (next1[1] == 'b') // [?,b]
    350 				&& (next2 != null) && (next2[1] === undefined); // [?,undefined]
    351 		}, function(game, current, next1, next2) {
    352 			//game.player.position = next1.position;
    353 			var p = current[1];
    354 			var b = next1[1];
    355 			current.splice(1, 1);// delete starting at 1 a single (1) element;
    356 			next1[1] = p;
    357 			next2[1] = b;
    358 			game.player = game.player.offsetX(1); // HACK!
    359 		}),
    360 		
    361 		new Rule('move', function(game, current, next1, next2) {
    362 			return (current[1] == 'p') // [?,p]
    363 				&& (next1 != null) && (next1[1] === undefined); // [?,undefined]
    364 		}, function(game, current, next1, next2) {
    365 			//game.player.position = next1.position;
    366 			var p = current[1];
    367 			current.splice(1, 1);// delete starting at 1 a single (1) element;
    368 			next1[1] = p;
    369 			game.player = game.player.offsetX(1); // HACK!
    370 		}),
    371 	];
    372 	this.event = function(name) {
    373 		this.events[name](this);
    374 	}
    375 }
    376 
    377 $(function() {
    378 	var canvas = document.getElementById('canvas');
    379     var c = canvas.getContext('2d');
    380 	c.fillStyle = 'black';
    381 	c.fillRect(0,0,16,16);
    382 
    383 	level = [
    384 		"hhhhhhhhhhwwwww",
    385 		"wwwwwwwwwwwfffw",
    386 		"wpfbfhffffffefw",
    387 		"wwwwwwwwwwwfffw",
    388 		"hhhhhhhhhhwwwww",
    389 		"fghsweb"// + 'p'
    390 	];
    391 	
    392 	loadSprites({
    393 		p: "img/player.png",
    394 		f: "img/floor.png",
    395 		g: "img/grass.png",
    396 		h: "img/hole.png",
    397 		s: "img/sand.png",
    398 		w: "img/wall.png",
    399 		b: "img/block.png",
    400 		e: "img/end.png",
    401 	}, function(sprites) {
    402 		var game = new Game(sprites);
    403 		window.game = game; // For debugging purposes.
    404 
    405 		// TODO : remove this and provide a GUI to manage cell's contents.
    406 		for (var y = 0; y < level.length; y++) {
    407 			for (var x = 0; x < level[y].length; x++) {
    408 				if (level[y][x] == 'p') {
    409 					game.player = new Position(x, y);
    410 				}
    411 				
    412 				if ('peb'.indexOf(level[y][x]) != -1) {
    413 					game.grid[x][y] = [ 'f', level[y][x], ];
    414 				} else {
    415 					game.grid[x][y] = [ level[y][x], ];
    416 				}
    417 			}
    418 		}
    419 
    420 		game.redraw = function() {
    421 			for (var x = 0; x < game.grid.width; x++) {
    422 				for (var y = 0; y < game.grid.height; y++) {
    423 					c.fillStyle = 'black';
    424 					c.fillRect(x*16, y*16, (x+1)*16, (y+1)*16);
    425 					var cell = game.grid[x][y];
    426 					for (o = 0; o < cell.length; o++) {
    427 						c.drawImage(sprites[cell[o]], x*16, y*16);
    428 					}
    429 				}
    430 			}
    431 		};
    432 		
    433 		game.redraw();
    434 
    435 		// Keyboard presses
    436 		$("body").keydown(function(e) {
    437 			switch(e.which) {
    438 			case 37: // left
    439 				game.event('left');
    440 				break;
    441 			case 38: // up
    442 				game.event('up');
    443 				break;
    444 			case 39: // right
    445 				game.event('right');
    446 				break;
    447 			case 40: // down
    448 				game.event('down');
    449 				break;
    450 			}
    451 			game.redraw();
    452 		});
    453 	});
    454 });
    455 
    456 // Concepts:
    457 // Actor ?
    458 // Object(s) in grid cell (= grid cell "state")
    459 // Sprite (grid cell + neighborhood are used to choose a cell, for example draw a "border" when the sprite's neighbor's state is different).
    460 
    461 // Concepts2:
    462 // Grid (2d-array of stuff), later: grid+paths/zones along which objects can move. Paths are drawn between the centers of two grid cells.
    463 // Tile (image)
    464 // Rule (includes a pattern, and a substitute(?))