jQuery - jTaquin

Petit jeu de taquin full js

Août 2009

En Août 2008, j'ai fabriqué ce petit jeu avec jQuery... c'était un challenge, car ça ne m'a jamais servi...

Bref je vous le mets ici pour ceux que ça intéresse, le code vaut ce qu'il vaut, depuis le temps je le referais autrement si j'avais à le mettre en prod. Mais si ça se trouve ça suffira largement à ceux qui seront intéressés...

Taquin

Je pars du principe que vous connaissez le jeu de Taquin... un jeu très simple où vous bougez les pièces pour reconstituer le puzzle.

L'image est personnalisable dans le code JS.

jTaquin.js

var jTaquin = {
	instances: [],
	options: [],
	lang: {
		'finish': 'Bravo, vous avez gagné',
		'restart': 'Recommencer',
		'coups': 'Coups',
		'time': 'Temps'
	},
	styles: {
		creux: {
			'border': '1px solid #222',
			'border-bottom': '1px solid #ccc',
			'border-right': '1px solid #ccc'
		},
		relief: {
			'border': '1px solid #ccc',
			'border-bottom': '1px solid #222',
			'border-right': '1px solid #222'
		},
		board: {
			'background-color': '#ccc',
			'font-family': 'Verdana, Arial',
			'font-size': '12px'
		},
		numbers: {
			'text-align': 'center',
			'font-family': 'Verdana, Arial',
			'font-size': '24px',
			'font-weight': 'bold'
		},
		empty_cell: {
			'background-color': '#ccc'
		}
	},
	create_cell: function(idx, i, opt) {
		var img;
		var div = document.createElement('div');
		var n = opt.size * opt.size;
		div.id = 'jTaquin'+idx+'_'+i;
		jQuery(div).css({
			'position': 'absolute',
			'top': Math.floor(i/opt.size)*(opt.cell_size + opt.bwidths),
			'left': (i%opt.size)*(opt.cell_size + opt.bwidths),
			'width': opt.cell_size+'px',
			'height': opt.cell_size+'px',
			'overflow': 'hidden',
			'display': 'block',
			'z-index': 9
		});
		if(i == n-1) {
			jQuery(div).css(jTaquin.styles.empty_cell).css({
				'z-index': 5
			});
		}
		jQuery(div).css(jTaquin.styles.creux);
		// events
		div.onclick = function() {
			jTaquin.click(idx, jTaquin.getIndex(idx, i));
		};
		div.onmouseover = function() {
			var instance = jTaquin.instances[idx];
			var index = jTaquin.getIndex(idx, i);
			if(!instance.finish && jTaquin.is_near(instance.empty, index, opt.size)) {
				div.style.cursor = 'pointer';
			}
			else {
				div.style.cursor = 'default';
			}
		};
		div.onselectstart = function() {
			return false;
		};
		// si numeros
		if(opt.numbers && i<n-1) {
			div.innerHTML = '<table cellpadding="0" cellspacing="0"><tr><td>'+i+'</td></tr></table>';
			jQuery(div).find('table').css({
				'position': 'absolute',
				'top': 0,
				'left': 0,
				'border': '0px',
				'width': '100%',
				'height': '100%'
			});
			jQuery(div).find('td').css(jTaquin.styles.numbers);
		}
		// si image
		if(opt.image && i<n-1) {
			img = document.createElement('img');
			img.id = 'jTaquin'+idx+'_img_'+i;
			img.src = opt.image;
			jQuery(img).css({
				'position': 'absolute',
				'top': -Math.floor(i/opt.size)*opt.cell_size,
				'left': -(i%opt.size)*opt.cell_size,
				'width': opt.width+'px',
				'height': opt.width+'px'
			});
			div.appendChild(img);
		}
		return div;
	},
	is_near: function(i_empty, i, size) {
		var dxy = jTaquin.get_dxy(i_empty, i, size);
		if(dxy.dy == 0 && (dxy.dx == 1 || dxy.dx == -1)) {
			return dxy;
		}
		if(dxy.dx == 0 && (dxy.dy == 1 || dxy.dy == -1)) {
			return dxy;
		}
		return false;
	},
	getIndex: function(idx, i) {
		var cells = jTaquin.instances[idx].cells;
		var n = cells.length;
		var index;
		for(index=0; index<n; index++) {
			if(cells[index] == i) {
				return index;
			}
		}
		return false;
	},
	getCell: function(idx, i) {
		return document.getElementById('jTaquin'+idx+'_'+i);
	},
	isFinish: function(idx) {
		var cells = jTaquin.instances[idx].cells;
		var n = cells.length;
		var i;
		for(i=0; i<n; i++) {
			if(cells[i] != i) {
				return false;
			}
		}
		return true;
	},
	click: function(idx, i) {
		var instance = jTaquin.instances[idx];
		var options = jTaquin.options[idx];
		var dxy = jTaquin.is_near(instance.empty, i, options.size);
		var cells = instance.cells;
		var cell = jTaquin.getCell(idx, cells[i]);
		var new_cell, save;
		var new_i = i;
		if(dxy && !instance.finish) {
			if(!instance.shuffle && instance.coups.length == 0) {
				instance.start = new Date();
			}
			if(dxy.dx != 0) {
				new_i = i - dxy.dx;
			}
			else {
				new_i = i - dxy.dy*options.size;
			}
			if(new_i == instance.empty) {
				if(!instance.shuffle) {
					instance.coups.push(new_i);
				}
				
				new_cell = jTaquin.getCell(idx, cells[new_i]);
				
				var pos = {top: cell.style.top, left: cell.style.left};
				var pos_new = {top: new_cell.style.top, left: new_cell.style.left};
				if(!instance.shuffle) {
					jQuery(new_cell).css(pos);
					jQuery(cell).animate(pos_new, 150, 'linear', function() {
						if(!instance.shuffle && jTaquin.isFinish(idx)) {
							alert(jTaquin.lang.finish);
						}
					});
				}
				else {
					jQuery(new_cell).css(pos);
					jQuery(cell).css(pos_new);
				}
				
				save = cells[new_i];
				cells[new_i] = cells[i];
				cells[i] = save;
				
				instance.empty = i;
			}
			if(!instance.shuffle && jTaquin.isFinish(idx)) {
				window.clearInterval(instance.interval);
				if(options.onFinish) {
					options.onFinish();
				}
				instance.finish = true;
			}
		}
	},
	shuffle: function(idx) {
		var instance = jTaquin.instances[idx];
		var options = jTaquin.options[idx];
		var cells = instance.cells;
		var n = options.size*options.size;
		var i, rand, empty;
		for(i=0; i<options.size; i++) {
			jTaquin.shuffle_snake(idx);
		}
		i = 0;
		while(i < 30*n) {
			empty = instance.empty;
			rand = Math.floor(Math.random()*4);
			switch(rand) {
				case 0: empty--; break;
				case 1: empty++; break;
				case 2: empty -= options.size; break;
				case 3: empty += options.size; break;
			}
			if(empty >= 0 && empty < n) {
				jTaquin.click(idx, empty);
				i++;
			}
		}
		jTaquin.shuffle_end(idx);
	},
	shuffle_snake: function(idx) {
		var instance = jTaquin.instances[idx];
		var options = jTaquin.options[idx];
		var i, k;
		var empty = instance.empty;
		var sens = -1;
		for(i=0; i<options.size; i++) {
			for(k=0; k<options.size-1; k++) {
				empty += sens;
				jTaquin.click(idx, empty);
			}
			if(i < options.size-1) {
				sens = -sens;
				empty -= 4;
				jTaquin.click(idx, empty);
			}
		}
		jTaquin.shuffle_end(idx);
	},
	shuffle_end: function(idx) {
		var instance = jTaquin.instances[idx];
		var options = jTaquin.options[idx];
		var empty, pt;
		empty = instance.empty;
		pt = jTaquin.get_xy(empty, options.size);
		if(options.size%2 == 0) {
			while(pt.x < options.size - 1) {
				pt.x++;
				empty++;
				jTaquin.click(idx, empty);
			}
		}
		while(pt.y < options.size - 1) {
			pt.y++;
			empty += options.size;
			jTaquin.click(idx, empty);
		}
		if(options.size%2 != 0) {
			while(pt.x < options.size - 1) {
				pt.x++;
				empty++;
				jTaquin.click(idx, empty);
			}
		}
	},
	get_xy: function(i, size) {
		return { x: i%size, y: Math.floor(i/size) };
	},
	get_dxy: function(i_empty, i, size) {
		var y_empty = Math.floor(i_empty/size);
		var x_empty = i_empty%size;
		var y = Math.floor(i/size);
		var x = i%size;
		return { dx: x - x_empty, dy: y - y_empty };
	},
	init: function(taquin, options, idx) {
		var i, n, opt, divs;
		// init
		jTaquin.options[idx] = {};
		jTaquin.instances[idx] = {};
		
		// options
		opt = {
			width: 400,
			size: 4,
			image: null,
			numbers: true,
			onFinish: null,
			help: true,
			help_size: 150,
			board_height: 70
		};
		if(typeof(options) == 'object') {
			for(i in options) {
				opt[i] = options[i];
			}
		}
		
		// others options
		if(opt.image && !options.numbers) {
			opt.numbers = false;
		}
		
		if(jQuery.browser.msie) {
			opt.bwidths = 0;
		}
		else {
			opt.bwidths = 2;
		}
		
		if(opt.size < 2) {
			opt.size = 2;
		}
		else if(opt.size > 10) {
			opt.size = 10;
		}
		
		// instance
		n = opt.size * opt.size;
		jTaquin.instances[idx] = {
			coups: [],
			empty: n-1,
			cells: [],
			interval: null,
			board: null,
			start: null,
			shuffle: true,
			finish: false,
			taquin: taquin,
			dom_coups: null,
			dom_time: null
		}
		
		// cree les cases
		opt.cell_size = Math.floor(opt.width/opt.size);
		opt.taquin_size = (opt.cell_size+opt.bwidths)*opt.size+2-opt.bwidths;
		taquin.innerHTML = '';
		jQuery(taquin).css({
			'position': 'relative',
			'width': opt.taquin_size+'px',
			'height': opt.taquin_size+'px'
		}).css(jTaquin.styles.empty_cell);
		jQuery(taquin).css(jTaquin.styles.relief);
		divs = [];
		for(i=0; i<n; i++) {
			divs.push(jTaquin.create_cell(idx, i, opt));
			jTaquin.instances[idx].cells.push(i);
		}
		
		// store options
		jTaquin.options[idx] = opt;
		
		// fill
		taquin.innerHTML = '';
		if(opt.help && opt.image) {
			var help = document.createElement('div');
			jQuery(help).css({
				'position': 'absolute',
				'top': opt.board_height+4,
				'left': opt.taquin_size+4,
				'width': opt.help_size+'px',
				'height': opt.help_size+'px',
				'overflow': 'hidden'
			});
			jQuery(help).css(jTaquin.styles.relief);
			var help_img = document.createElement('img');
			help_img.src = opt.image;
			jQuery(help_img).css({
				'width': (opt.help_size-4+opt.bwidths)+'px',
				'height': (opt.help_size-4+opt.bwidths)+'px',
				'overflow': 'hidden'
			});
			jQuery(help_img).css(jTaquin.styles.creux);
			help.appendChild(help_img);
			taquin.appendChild(help);
		}
		
		// board
		var board = document.createElement('div');
		jQuery(board).css({
			'position': 'absolute',
			'top': -1,
			'left': opt.taquin_size+4,
			'width': opt.help_size+'px',
			'height': opt.board_height+'px',
			'overflow': 'hidden'
		});
		jQuery(board).css(jTaquin.styles.relief);
		var board_div = document.createElement('div');
		jQuery(board_div).css({
			'width': (opt.help_size-2)+'px',
			'height': (opt.board_height-2)+'px',
			'overflow': 'hidden'
		});
		jQuery(board_div).css(jTaquin.styles.creux);
		jQuery(board_div).css(jTaquin.styles.board);
		board_div.innerHTML = '<div>'+jTaquin.lang.coups+': <strong>0</strong></div><div>'+jTaquin.lang.time+': <strong>0</strong> s</div><div style="text-align:center"><button onclick="jTaquin.reset('+idx+');">'+jTaquin.lang.restart+'</button></div>';
		jQuery(board_div).find('div').css({'padding':'2px'});
		jTaquin.instances[idx].dom_coups = jQuery(board_div).find('strong').get(0);
		jTaquin.instances[idx].dom_time = jQuery(board_div).find('strong').get(1);
		board.appendChild(board_div);
		taquin.appendChild(board);
		jTaquin.instances[idx].board = board_div;
		
		// cells
		for(i=0; i<n; i++) {
			taquin.appendChild(divs[i]);
		}
		
		// shuffle
		jTaquin.shuffle(idx);
		
		jTaquin.instances[idx].shuffle = false;
		jTaquin.instances[idx].interval = window.setInterval(function() {
			var instance = jTaquin.instances[idx];
			if(instance.board) {
				var n = jTaquin.instances[idx].coups.length;
				var d = new Date();
				instance.dom_coups.innerHTML = n;
				if(jTaquin.instances[idx].start) {
					d = Math.round((d-jTaquin.instances[idx].start)/100)/10;
					if(Math.floor(d) == d) {
						d = d+'.0';
					}
					instance.dom_time.innerHTML = d;
				}
			}
		}, 100);
	},
	reset: function(idx) {
		var instance = jTaquin.instances[idx];
		var options = jTaquin.options[idx];
		jTaquin.stop(idx);
		jTaquin.init(instance.taquin, options, idx);
		return true;
	},
	stop: function(idx) {
		var interval = jTaquin.instances[idx].interval;
		if(interval) {
			window.clearInterval(interval);
		}
		jTaquin.options[idx] = {};
		jTaquin.instances[idx] = {};
	}
};
/******************************************************************************/
jQuery.fn.Taquin = function(options) {
	return this.each(function() {
		jTaquin.init(this, options, jTaquin.instances.length);
	});
};

Téléchargement

jTaquin.zip

Liens

Voir la démo