[JavaScript] [作品]  读心术系列之卡片读心术原理及Web实现 6张卡片

小时候在肯德基买儿童套餐的时候发现一种玩具,里面是6张写着很多数字的卡片,先让观众心里默想一个1~50的数字,然后拿着每张卡片去让测试的人回答卡片上是否有所想的数字。当6张卡片问完之后,表演者就能知道观众所想的数字。

当我知道这个玩具的神奇之处之后,我就拿着它向班里面的每个同学表演,同学们都觉得很神奇,但是并不知道其中的原理。

直到有一天,卡片找不到了,于是我想着靠自己的记忆把这套卡片复原。还好我记得表演的方法,是把观众回答“有”的卡片所代表的数字全部加起来,就是所想的数字。

首先是卡片所代表的数字。因为最后是要把卡片所代表的数字加起来,所以这些数字必须是能组合成1~50之间所有数字的一组数。组成1需要数字1,组成2需要数字2,组成3需要数字1和2,4需要4,5需要1和4,6需要2和4。以此类推,这组数字是【1、2、4、8、16、32】,6个数字刚好对应6张卡片。其实这个和Linux下文件的权限是一样的,文件权限有执行,写入,读取3种,对应数字1,2,4。如果将来还有一个新的权限,那肯定是8。以下是1到20组合所需的数字。

1到20组成所需的数字


然后是上面这些代表数字,所有需要这些代表数的数字,比如需要1的数字有1、3、5、7、9...,需要2的数字有2、3、6、7、10、11...,假设代表数是n,不难发现,数字的规律是先从n本身开始连续n个数,然后跳过n个数,再连续n个数。如下图:


把这些数字,分别放到6张卡片当中,卡片的原理复原算是完成了,如下图。卡片中最小的数字即代表数。


接下来是Web实现。使用HTML + CSS + jQuery。

代表数,数字范围和结果的变量定义。及卡片颜色和一些用于响应式的变量定义。

JavaScript

	var need = [1, 2, 4, 8, 16, 32].sort(randomsort); //组合所需数字
	var total = 60; //数字范围
	var result = 0; //结果
	
	var colors = ['#009e96', '#22ac38', '#eb6100', '#eb6877', '#8957a1', '#b7aa00'].sort(randomsort); //卡片颜色
	var transform, cardMargin, clickCount = 0;
	var cardHeight;


构造每个卡片上的数字

JavaScript

function get_nums_obj(){
	var obj = [];
	for( i = 0; i < need.length; i++ ){
		var numbers = [];
		var pointer = need[i];
		
		for( j = need[i]; j <= total; j++ ){
			numbers.push(j.toString());
			
			//如果连续need[i]个数字
			if( j - pointer === need[i] - 1 ){
				pointer = j + need[i] + 1; //跳过need[i]个数字
				j = pointer - 1; //j在下轮循环自动增长,故-1
			}
		}
		obj['N'+need[i]] = numbers.sort(randomsort);
	}
	return obj;
}


数组的随机排序

JavaScript

function randomsort(a, b){
	//用Math.random()函数生成0~1之间的随机数与0.5比较,返回-1或1
	return Math.random()>.5 ? -1 : 1;
}


卡片视图的生成

JavaScript

function generate_view(){
	var obj = get_nums_obj();
	var count = 0;
	for( var item in obj ){
		var index = item.substring(1);
		
		//$('#card-content .card-inner .card-inner-step').append('<div class="step card" style="background-color:'+colors[count]+';" data-num="'+index+'"></div>');
		$('#card-content .card-inner .card-inner-step').append('<div class="step card" style="background-color:'+colors[count]+';"></div>');
		$.each(obj[item], function(index, num){
			$('#card-content .card-inner .card:last').append('<span class="card-num">'+num+'</span>');
		});
		count++;
	}
}


卡片滚动动画

JavaScript

function scrollCard(){
	if( clickCount >= $('#card-content .step').length - 1 ) return;
	
	$('#card-content .card-inner').css('transform', 'translateY(-'+transform+'px)');
	transform += cardHeight;
	clickCount++;
}


重置

JavaScript

function resetCard(){
	clickCount = 0;
	result = 0;
	need = need.sort(randomsort);
	colors = colors.sort(randomsort);
	transform = cardHeight;
	$('#card-content .card-inner .card-inner-step').html('');
	generate_view();
	
	$('#card-content .card-inner').css('transform', 'translateY(0px)');
	$('.action-reset').fadeOut(100, function(){
		$('.action-start').fadeIn(100);
	});
}


卡片高度适应

JavaScript

function resizeStep(){
	$('.card-wapper .step').height($('.card-wapper .card:first').height());
	$('.card-wapper').height($('.card-wapper .step:first').outerHeight() + cardMargin * 2);
}


初始化

JavaScript

function init(){
	generate_view();
	cardMargin = parseInt($('.card-wapper .card:first').css('margin-bottom').replace('px', ''));
	cardHeight = $('.card-wapper .card:first').outerHeight() + cardMargin * 2;
	transform = cardHeight;
	resizeStep();
	
	$(window).resize(function(){
		resizeStep();
	});
	
	//“是”“否”按钮点击
	$('.action-ask button').click(function(){
		//加上当前卡片数字
		if( $(this).hasClass('yes') ){
			//result += parseInt($('.card-inner-step .card:eq('+(clickCount-1)+')').data('num'));
			result += need[ clickCount-1 ];
		}
		
		scrollCard();
		if( clickCount >= $('#card-content .step').length - 1 ){
			$('#result-num').text(result);
			
			$('.action-ask').fadeOut(100, function(){
				$('.action-reset').fadeIn(100);
			});
		}
	});
	
	//开始按钮点击
	$('.action-start #start').click(function(){
		scrollCard();
		$('.action-start').fadeOut(100, function(){
			$('.action-ask').fadeIn(100);
		});
	});
	
	//重置按钮点击
	$('.action-reset #reset').click(function(){
		resetCard();
	});
}


不过这种程序生成的卡片没有实体卡片的效果好,因为当用户点击“是”或“否”的时候,用程序很轻松地就能把一张卡片上的数字全部排除,容易让别人误以为是用了排除法;而实体卡片要用排除法却很难,况且在表演过程中排除数字非常容易让观众看出来。


源码下载:ThoughtReader-Card-20170527.zip

演示地址:https://www.qwqoffice.com/thought-reader-card/


发表您的留言