chriswarbo-net: aa57694334920ef41a1ed24fee2f8a9435b264d9

     1: window.crossover_population = [];
     2: window.crossover_blobs = [];
     3: window.gradient = [];
     4: 
     5: // Literate Programming merge sort (stable, in-place sort)
     6: function msort(array, begin, end)
     7: {
     8: 	var size=end-begin;
     9: 	if(size<2) return;
    10: 
    11: 	var begin_right=begin+Math.floor(size/2);
    12: 
    13: 	msort(array, begin, begin_right);
    14: 	msort(array, begin_right, end);
    15: 	merge_inplace(array, begin, begin_right, end);
    16: }
    17: 
    18: function merge_sort_inplace(array)
    19: {
    20: 	msort(array, 0, array.length);
    21: }
    22: 
    23: function merge_sort(array,comparison)
    24: {
    25: 	if(array.length < 2)
    26: 		return array;
    27: 	var middle = Math.ceil(array.length/2);
    28: 	return merge(merge_sort(array.slice(0,middle),comparison),
    29: 			merge_sort(array.slice(middle),comparison),
    30: 			comparison);
    31: }
    32: 
    33: function merge_sort(array,comparison)
    34: {
    35: 	if(array.length < 2)
    36: 		return array;
    37: 	var middle = Math.ceil(array.length/2);
    38: 	return merge(merge_sort(array.slice(0,middle),comparison),
    39: 			merge_sort(array.slice(middle),comparison),
    40: 			comparison);
    41: }
    42: 
    43: Array.prototype.swap=function(a, b)
    44: {
    45: 	var tmp=this[a];
    46: 	this[a]=this[b];
    47: 	this[b]=tmp;
    48: }
    49: 
    50: function insert(array, begin, end, v)
    51: {
    52: 	while(begin+1<end && array[begin+1]<v) {
    53: 		array.swap(begin, begin+1);
    54: 		++begin;
    55: 	}
    56: 	array[begin]=v;
    57: }
    58: 
    59: function merge(left,right,comparison)
    60: {
    61: 	var result = new Array();
    62: 	while((left.length > 0) && (right.length > 0))
    63: 	{
    64: 		if(comparison(left[0],right[0]) <= 0)
    65: 			result.push(left.shift());
    66: 		else
    67: 			result.push(right.shift());
    68: 	}
    69: 	while(left.length > 0)
    70: 		result.push(left.shift());
    71: 	while(right.length > 0)
    72: 		result.push(right.shift());
    73: 	return result;
    74: }
    75: 
    76: // End merge sort
    77: 
    78: $(function() {
    79: 	$('#crossover_playfield').svg({onLoad: crossover_init});
    80: 	$('#crossover_playfield').click(function(){
    81: 		$('#crossover_playfield').unbind('click');
    82: 		crossover_step();
    83: 	});
    84: 	$('#crossover_population').change(function(){
    85: 		$('#crossover_population_display').text($('#crossover_population').val());
    86: 	});
    87: 	$('#crossover_population').change();
    88: 
    89: 	$('#crossover_fitness').text('0');
    90: 
    91: 	$('#crossover_rate').change(function(){
    92: 		$('#crossover_rate_display').text(Math.round($('#crossover_rate').val()));
    93: 	});
    94: 	$('#crossover_rate').change();
    95: });
    96: 
    97: crossover_init = function(svg) {
    98: 	_.times(Math.round(Math.random() * 100 + 1), function(){
    99: 		var l = Math.random();
   100: 		var r = Math.random();
   101: 		var t = Math.random();
   102: 		var b = Math.random();
   103: 		var temp;
   104: 		if (l > r) {
   105: 			temp = l;
   106: 			l = r;
   107: 			r = temp;
   108: 		}
   109: 		if (t > b) {
   110: 			temp = t;
   111: 			t = b;
   112: 			b = temp;
   113: 		}
   114: 		window.gradient.push(function(x, y, draw){
   115: 			var w = $('#crossover_playfield').width();
   116: 			var h = $('#crossover_playfield').height();
   117: 			if (draw) {
   118: 				$('#crossover_playfield').svg('get').rect(l*w, t*h, (r-l)*w, (b-t)*h, {'fill': 'white', 'fill-opacity': 0.02});
   119: 			}
   120: 			else {
   121: 				if ((l < x / w) && (r > x / w) && (t < y / h) && (b > y / h)) {
   122: 					return 1;
   123: 				}
   124: 				return 0;
   125: 			}
   126: 		});
   127: 	});
   128: 	$('#crossover_playfield').svg('get').rect(0,0,$('#crossover_playfield').width(),$('#crossover_playfield').height(),{'fill': 'black'});
   129: 	_.each(window.gradient, function(a){a(0, 0, true);});
   130: }
   131: 
   132: fitn_reduce = function(a,b) { return a + b; };
   133: fitn = _.memoize(function(z) {
   134: 	var x = z[0];
   135: 	var y = z[1];
   136: 	if ((x < 0) || (x > $('#crossover_playfield').width())) { return 0; }
   137: 	if ((y < 0) || (y > $('#crossover_playfield').height())) { return 0; }
   138: 	return _.reduce(_.map(window.gradient, function(a){ return a(x, y, false); }), fitn_reduce);
   139: });
   140: 
   141: // Compare 2 solutions based on their fitness
   142: comp = function (a, b) {
   143: 	var fit_a = fitn(a['solution']);
   144: 	var fit_b = fitn(b['solution']);
   145: 	if (fit_a < fit_b) {
   146: 		return -1;
   147: 	}
   148: 	if (fit_a > fit_b) {
   149: 		return 1;
   150: 	}
   151: 	return 0;
   152: };
   153: 
   154: crossover = function(a, b) {
   155: 	if (typeof a === 'undefined' || typeof b == 'undefined') { return 0; }
   156: 	// Duplicate the parents, turning [x, y] into xy
   157: 	var child1 = (512 * a['solution'][0]) + a['solution'][1];
   158: 	var child2 = (512 * b['solution'][0]) + b['solution'][1];
   159: 	var w = $('#crossover_playfield').width();
   160: 	var h = $('#crossover_playfield').height();
   161: 	var crossover_points = [];
   162: 	_.times($('#crossover_rate').val(), function() {
   163: 		do {
   164: 			point = Math.round(Math.random() * 16.0);
   165: 		} while ($.inArray(point, crossover_points) !== -1);
   166: 		crossover_points.push(point);
   167: 	});
   168: 	_.each(crossover_points, function(p) {
   169: 		var point = Math.pow(2, p);
   170: 		var section1 = child1 % point;
   171: 		var section2 = child2 % point;
   172: 		child1 = child1 - section1 + section2;
   173: 		child2 = child2 - section2 + section1;
   174: 	});
   175: 	// Now split apart the x and y again
   176: 	var temp = child1 % 512;
   177: 	child1 = {
   178: 		solution:[temp, (child1 - temp) / 512],
   179: 		blob:$('#crossover_playfield').svg('get').circle(temp, (child1-temp)/512, 2, {fill: 'red'})
   180: 	};
   181: 	temp = child2 % 512;
   182: 	child2 = {
   183: 		solution:[temp, (child2 - temp) / 512],
   184: 		blob:$('#crossover_playfield').svg('get').circle(temp, (child2-temp)/512, 2, {fill: 'red'})
   185: 	};
   186: 	return [child1, child2];
   187: }
   188: 
   189: window.root2 = Math.sqrt(2);
   190: 
   191: window.get_mid = _.memoize(function(top, bottom) {
   192: 	return Math.ceil((top - bottom) / window.root2);
   193: });
   194: 
   195: window.reproduce = function() {
   196: 	// Choose a parent probabilistically. We divide our population in 2
   197: 	// over and over until we narrow down a single solution.
   198: 	var top = window.crossover_population.length;
   199: 	var bottom = 0;
   200: 	// This function finds the point x such that the probability from
   201: 	// bottom to x is equal to the probability from x to top
   202: 	var mid;
   203: 	var parents = [];
   204: 	// Beware while loops! Do as little as possible in them.
   205: 	do {
   206: 		top = window.crossover_population.length;
   207: 		bottom = 0;
   208: 		while (top - bottom > 1) {
   209: 			mid = get_mid(top, bottom);
   210: 			if (Math.random() > 0.5) {
   211: 				top = mid;
   212: 			}
   213: 			else {
   214: 				bottom = mid;
   215: 			}
   216: 		}
   217: 		bottom = Math.ceil(bottom);
   218: 		if ($.inArray(bottom, parents) === -1) {
   219: 			parents.push(Math.ceil(bottom));
   220: 		}
   221: 	} while (parents.length < 2);
   222: 	// We have 2 parents, perform a crossover
   223: 	var children = crossover(window.crossover_population[parents[0]], window.crossover_population[parents[1]]);
   224: 	window.new_solutions.push(children[0]);
   225: 	window.new_solutions.push(children[1]);
   226: };
   227: 
   228: window.draw_blobs = function(x) {
   229: 	var solution = window.crossover_population[window.crossover_population.length - 1];
   230: 	window.crossover_blobs.push(
   231: 		$('#crossover_playfield').svg('get').circle(solution['solution'][0], solution['solution'][1], 5, {fill: 'lime'})
   232: 	);
   233: };
   234: 
   235: crossover_step = function() {
   236: 	// Grab our population first (if we keep reading the input, we
   237: 	// may get inconsistent values)
   238: 	var pop = parseInt($('#crossover_population').val());
   239: 
   240: 	// We are swapping 1/4 of the population each time
   241: 	var to_swap = Math.ceil(pop / 4.0);
   242: 
   243: 	window.new_solutions = [];
   244: 
   245: 	// Bulk out the population if the slider has been increased
   246: 	if (pop > window.crossover_population.length) {
   247: 		window.crossover_population.reverse();
   248: 		window.crossover_population.push.apply(window.crossover_population, _.map(_.range(pop - window.crossover_population.length), function(x) {
   249: 			return {
   250: 				solution:[Math.round(Math.random()*$('#crossover_playfield').width()),Math.round(Math.random()*$('#crossover_playfield').height())],
   251: 				blob:false
   252: 			};
   253: 		}));
   254: 		window.crossover_population.reverse();
   255: 	}
   256: 
   257: 	_.times(Math.ceil(to_swap / 2), window.reproduce);
   258: 
   259: 	// Sort the population by fitness
   260: 	if ($('#crossover_stable').is(':checked')) {
   261: 		window.crossover_population = merge_sort(window.crossover_population, comp);
   262: 	}
   263: 	else {
   264: 		window.crossover_population.sort(comp);
   265: 	}
   266: 
   267: 	$('#crossover_fitness').text(fitn(window.crossover_population[window.crossover_population.length - 1]['solution'])+"");
   268: 
   269: 	// At this point we have a sorted array, so draw our blobs
   270: 	while (window.crossover_blobs.length > 0) {
   271: 		$('#crossover_playfield').svg('get').remove(window.crossover_blobs.shift());
   272: 	}
   273: 
   274: 	window.draw_blobs(0);
   275: 
   276: 	// Throw away any excess solutions and the least-fit solutions
   277: 	var s;
   278: 	while (window.crossover_population.length > pop) {
   279: 		window.mid_points = [];
   280: 		s = window.crossover_population.shift();
   281: 		if (s['blob']) {
   282: 			$('#crossover_playfield').svg('get').remove(s['blob']);
   283: 		}
   284: 	}
   285: 
   286: 	// Add the new solutions to the mix
   287: 	window.crossover_population.reverse();
   288: 	window.crossover_population.push.apply(window.crossover_population, window.new_solutions);
   289: 	window.crossover_population.reverse();
   290: 
   291: 	window.setTimeout(function(){ crossover_step(); }, 10);
   292: }

Generated by git2html.