今天打开微博,发现还有人在做这个游戏,希望TX的童鞋不要介意把一份不标准答案公布出来(如果有必要,我可以删除,呐,校招求职的童鞋,请不要盲目抄写,因为我的答案或许是最差的解题思路哟)。 那么我就先用常规解题思路走一遍,然后按照取巧偷懒的方式跑一遍,开打!开打!
答题网址
第一关
题目描述: 一个中规中矩的登录框,先不看代码,首先什么都不填写,点击提交试试看,诶,提示我们“姓名和邮箱是你的闯关凭证,不能为空”,哦,那么随便填写一个吧,出于对作者的尊重,咱们写个真的吧。^ ^ 填写之后,在点击提交,会提示“打不开?抓一下包看看……”,腾讯组的童鞋只是在过滤不用chrome inspector/fiddle的童鞋嘛。 F12后,继续点击按钮。看到按钮事件会向http://codestar.alloyteam.com/signup
地址提交一个POST请求, 而一般的返回结果为:
{"retcode":10001,"result":"请设置隐藏域的值"}
提示有点太明确了吧,那么看看页面结构吧,发现表单果然有一个字段timestamp隐藏域value木有赋值。
<div id="board">
<a id="close" href="#">×</a>
<div class="info">输入个人信息,打开后门门锁</div>
<h2>后 门</h2>
<form class="mt30" id="msgForm">
<input type="text" name="name" placeholder="姓名" />
<input type="text" name="email" placeholder="邮箱(后面用到)" />
<input type="hidden" name="timestamp" />
<div class="mt30"><button class="btn" id="btnSubmit">打开</button></div>
</form>
</div>
翻看页面内的内联脚本,common.js,没有看到对时间格式的检查相关的函数,那么就默认需求的时间格式是纯数字的毫秒数好了(错了再换格式呗。)
解题答案
document.getElementsByName('name')[0].setAttribute('value','soulteary'),
document.getElementsByName('email')[0].setAttribute('value','soulteary@qq.com'),
document.getElementsByName('timestamp')[0].setAttribute('value',(new Date-0)),
document.getElementById('btnSubmit').click();
第二关
题目描述: 点开题目,明显的是之前让大家眼前一亮的CSS画图,看了一下图形,考察点是椭圆(圆角),图形旋转以及三角形实现。 查看页面源码,发现腾讯童鞋很好心的放了一个参考链接,那么就简单的看一下吧。 http://www.alloyteam.com/2012/10/css3-draw-qq-logo/ 根据图上左边的椭圆给的数据,宽40,高60,那么得到补充规则
#model_1{
border-top-left-radius: 20px 30px;
border-top-right-radius: 20px 30px;
border-bottom-right-radius: 20px 30px;
border-bottom-left-radius: 20px 30px;
}
/* 或者 下面的规则*/
#model_1{
border-radius:50% 50%;
}
而右边的图形,根据60deg等提示得到下面的规则,这部分题不严谨,其实可以单纯的设置border-bottom-color和border-top-color来实现三角形,但是有一个不错的细节点是按照右边的那个点为原点旋转。
#model_2 {
-webkit-transform: rotate(-60deg);
border-bottom:10px solid transparent;
border-top: 20px solid transparent;
-webkit-transform-origin: top right;
}
解题如下:
document.getElementById('textarea_1').innerHTML = 'border-radius:50% 50%;',
document.getElementById('textarea_2').innerHTML = '-webkit-transform: rotate(-60deg);border-bottom:10px solid transparent;border-top: 20px solid transparent;-webkit-transform-origin: top right;',
document.getElementById("container").onkeyup({target:document.getElementById('textarea_1'),id:'textarea_1'}),
document.getElementById("container").onkeyup({target:document.getElementById('textarea_2'),id:'textarea_2'});
第三关
题目描述: 应该是考察快速使用api的的能力以及如何快速完成任务吧。 到坦克战场后,你发现首先你是没有坦克的,那么创建一个好了,编辑你的坦克,发现你的坦克的各种事件都是空的(当然是空的) 翻开文档,发现支持的参数还蛮多的,包括按下按键以及发送消息等,但是真心不想写WASD或者上下左右的事件了…好在腾讯的童鞋也没有较真非要写,而且他们的demo坦克比较菜… 那么我们搞一个固定炮台过关好了,将demo中的开火以及旋转车头复制进你的坦克事件中,坐等过关吧。
/**
*robot主循环
**/
run:function(){
this.loop(function(){
this.say("小样,能打到我么?","deepskyblue");
this.setTurn(60,function(){
this.setTurn(-120,function(){
this.setTurn(240,function(){
this.setTurn(-120);
this.execute();
});
this.execute();
});
this.execute();
});
this.execute();
})
},
/**
*看到其他robot的处理程序
**/
onScannedRobot:function(e){
this.fire(1);
}
第四关
题目描述: 这个大概是对常见问题的掌握和理解吧,多数问题都是大家讨论烂掉吧,不过或许出题人想看一些非主流答案,或者对效率苛刻一下? //完整copy对象,用个简单的实现吧(这道题的出题者想要的是深拷贝的写法^ ^)
JSON.parse(JSON.stringify(arr));
//写一个常规的,这个效率不是最优的,一会再写一篇这个效率的blog吧
s.replace(/(^\s*)|(\s*$)/g, "");
//写一个常规的,这个效率不是最优的,一会再写一篇这个效率的blog吧
s.replace(/(^\s*)|(\s*$)/g, "");
//最后一个利用slice特性将类数组转换
Array.prototype.slice.call(list,0);
第五关
题目描述: 考察分析能力以及简单的数学能力(动态规划),这个页面中的js我加了注释先写在下面吧。 里面有好几处细节写的超级棒!(yin dang,who’s author?)
(function() {
//创建ajax请求,判断是否过关
function resultChecker() {
ajax("/pass", {method: "POST",data: '{"q":1,"s":5,"_t":' + token(v) + "}",contentType: "application/json",onSuccess: function() {
hideBoard();
document.getElementById("btnNext").className += " show";
alert("\u8fc7\u5173\uff01\u4e0b\u4e00\u5173\u7684\u5165\u53e3\u5df2\u6253\u5f00")
}})
}
//提示剩余次数
function showOverage() {
//提示剩余次数,如果没次数了,就刷新页面
n.innerHTML = f;
n.parentNode.title = "\u8fd8\u6709 " + f + " \u6b21\u5c1d\u8bd5\u7684\u673a\u4f1a";
k.className = "tada";
0 >= f && (alert("\u8b66\u94c3\u5927\u54cd\uff0c\u95ef\u5173\u5931\u8d25\uff01"), location.reload())
}
//计分函数
function theScore() {
var openBox = l.getElementsByClassName("open");
//重置分数
e = 0;
for (var a = openBox.length; a--; )
e += Number(openBox[a].innerHTML);
//显示计分更新
w.innerHTML = e
}
//每个元素的点击事件
function clickBox(elem) {
//失败提示重置(隐藏)
failBox.style.visibility = "hidden";
//获取点击元素的row和id,当前行的开始和结束
//把同一行的元素的选中状态重置掉
for (var row = Number(elem.parentNode.id.slice(4)), id = Number(elem.id.slice(7)),
start = row * (row - 1) / 2 + 1, end = (row + 1) * (row + 1 - 1) / 2 + 1;
start < end; start++)
{
document.getElementById("folder_" + start).classList.remove("open");
}
//给当前元素添加选中状态
elem.classList.add("open");
//如果不是最后一行的元素
if (12 !== row) {
//重置选中元素下面所有的节点的状态
for (start = (row + 1) * (row + 1 - 1) / 2 + 1; 78 >= start; start++)
document.getElementById("folder_" + start).className = "folder";
//设置选中元素的子节点可以选择
document.getElementById("folder_" + (row + id)).classList.add("able");
document.getElementById("folder_" + (row + id + 1)).classList.add("able");
//计算当然的数值结果
theScore()
} else{
//已经到了最后一行了,那么触发对查找结果求和的事件,计算结果并和答案对比,如果一致,那么调用AJAX检查是否过关
//反之,显示查找失败,寻找次数减一,更新剩余查找次数提示
theScore(), s === e ? resultChecker() : (failBox.style.visibility = "visible", f--, showOverage())
}
}
//计算过关的token
function token(key) {
for (var mask = 5381, index = 0, count = key.length; index < count; ++index){
//把key按位转换为ASCII数值
//mask移位并和key的每一位求和
mask += (mask << 5) + key.charAt(index).charCodeAt();
}
//获得最后的过关token
return mask & 2147483647
}
var v = log.innerHTML, //过关的KEY
g = [],//全局的随机数字数组
l = document.getElementById("box"), //模版容器
w = document.getElementById("currValue"),
failBox = document.getElementById("failBox"), //失败提示
s = 0, //这次随机数字的最大和
e = 0, //当前选择的元素获得的分数和
f = 2, //剩余次数计数器
k = document.getElementById("bell"), //尝试机会的整个元素
n = document.getElementById("times"), //提示剩余次数提示的行内元素
h = pfx("animation"); //酱油变量, 先储存前缀CSS属性
//先判断是神马浏览器,然后根据对象中设置的浏览器类型绑定动画结束事件,把bell的class清空
on(k, {moz: "animationend",webkit: "webkitAnimationEnd",
ms: "MSAnimationEnd",o: "oAnimationEnd"}[h.toLowerCase().replace("animation", "")], function() {
k.className = ""
});
//初始化记分
showOverage();
resultChecker()
//委托每个元素的点击事件到box上
l.onclick = function(elem) {
elem = elem.target;
//当这个元素可以被点击的情况下,触发这货的点击事件
elem.classList.contains("able") && clickBox(elem)
};
//创建DOM元素
(function() {
//用78个随机数填满全局数组g
for (var index = 1; 78 >= index; index++){
g[index] = Math.ceil(100 * Math.random()) + 100;
}
//把78个元素打散在12行中
for (var tpl = "", id = 0, index = 1; 12 >= index; index++) {
//每行放每行的序号个元素
for (var tpl = tpl + ('<div id="row_' + index + '">'), count = 1; count <= index; count++){
//把数组g中的内容填出来
id++, tpl += '<span class="folder" id="folder_' + id + '">' + g[id] + "</span>";
}
tpl += "</div>"
}
//填充box内容
l.innerHTML = tpl
})();
//预先计算最大值
(function() {
/**
* firstOne 每行开始的第一个元素
* index 某行中的某列元素的全局索引
* data 储存每个节点的最大叠加数值的容器
* row 当前行
* offset 当前元素的列增量
**/
for (var firstOne, index, data = [], row = 12; 0 < row; row--) {
firstOne = row * (row - 1) / 2 + 1;
//将所有有节点的父节点的数值和子节点求和并保存在当前节点
for (var offset = 0; offset < row; offset++){
index = firstOne + offset, data[index] = 12 === row ? g[index] : g[index] + (data[index + row] > data[index + row + 1] ? data[index + row] : data[index + row + 1])
}
}
//把结果保存到全局变量中
s = data[1]
})();
//选择第一个元素,标记其可以点击,并触发它的点击事件
h = document.getElementById("folder_1");
h.classList.add("able");
clickBox(h);
//干掉DOM中的KEY
clear();
//bla bla bla...
console.log("\n\u7b2c\u4e94\u7ae0\uff1a\u8d44\u6599\n");
console.log("\u201c\u8fd9\u4e2b\u7684\u662f\u8c01\u5efa\u7684\u6587\u4ef6\u5939\uff1f");
console.log("\u770b\u6765\u8981\u6309\u4e00\u5b9a\u987a\u5e8f\u6253\u5f00\uff0c\u4f7f\u5f97\u4fdd\u5bc6\u7b49\u7ea7\u6700\u9ad8\uff0c\u624d\u80fd\u627e\u5230\u6211\u8981\u7684\u8d44\u6599\u3002\u201d\n")
})();
看完代码,估计你也就知道我文章开头说的如何直接跳过做题一路next了吧… 但是我们还是继续把这个题做完吧,因为蛮好玩的! 实现工具如下:
//腾讯前端特工第五题,答题工具
var g=[],//页面中的数字元素
result = 0,
path = [];
//将页面中的元素还原为一个一维数组
var box = document.getElementById('box').childNodes;
for(var i=0, j=box.length; i<j; i++){
var row = box[i].childNodes;
var index = 0;//储存当前元素索引
for(var m=0, n=row.length; m<n; m++){
var start = (i+1)*(i+1-1)/2+1;
var index = start+m;
g[index] = parseInt(row[m].innerHTML);
}
}
//先把结果计算出来吧, 实现工具会更简单点
for(var firstOne, index, data = [], row = 12; 0 < row; row--) {
firstOne = row * (row - 1) / 2 + 1;
//将所有有节点的父节点的数值和子节点求和并保存在当前节点
for (var offset = 0; offset < row; offset++){
index = firstOne + offset;
if(row===12){
path[index] = [index];
data[index] = g[index];
}else{
var leftNode = data[index + row];
var rightNode = data[index + row +1];
if(leftNode>rightNode){
path[index] = [index, index+row];
}else{
path[index] = [index, index+row+1];
}
data[index] = g[index] + (leftNode > rightNode ? leftNode : rightNode);
}
}
}
result=data[1];
document.getElementById('currValue').innerHTML = result;
var start = path[1];
for(var i=0, row=12, step=1; i<row; i++){
step = path[step];
if(step[1]){step = step[1];}
document.getElementById("folder_" + step).setAttribute("class", "folder able open");
}
alert('我只能帮你到这里了,还愣着干嘛,点一下最后的那个数字,然后坐等邮件吧,如果你有微博的话,欢迎粉我 @soulteary');
但是腾讯前端小组的童鞋很机智的在common.js中声明了一个clear的函数,在页面中内联的脚本中把这货干掉了,并且这些都是在闭包中做的… 换言之,少年你直接写脚本搞不定滴水,当然如果你用fiddle之类的代理替换页面的也可以哟! 那么我们来用一种调试的思路快速搞定这些题吧!
先打开chrome调试工具,然后选择source,在当前HTML文档页面中找到压缩成一坨的代码,随便下个断, 然后在下面的Watch Expressions中添加一个变量“log”,然后刷新页面。
不出意外,浏览器中断了吧,查看我们监视的变量“log”的任何一个innerHTML/innerText/OuterHTML/OuterText,发现诸如这样的MD5字符串:“3b3d8a5f33d811ca00cf89bcdf03e6e1”,然后不要客气,复制出来吧。 (注意监视变量名称可以搜索源码中的log.innerHTML前的名称定义)
然后将页面源码中的诸如这样的代码复制出来。 以第一关为例,每一关由于压缩的原因,变量名不一致,但是方法是一致滴!
//把刚刚的key赋值一下,变量名为下面的ajax函数的data字段的实参名
var f = "3b3d8a5f33d811ca00cf89bcdf03e6e1"
//找到return 一个变量 & 一个字符串的函数
var e = function(c) {
for (var a = 5381, b = 0, d = c.length; b < d; ++b)
a += (a << 5) + c.charAt(b).charCodeAt();
return a & 2147483647
}
//找到/pass的ajax复制出来
ajax("/pass", {method: "POST",data: '{"q":1,"s":1,"_t":' + e(f) + "}",contentType: "application/json",onSuccess: function(c) {
hideBoard();
document.getElementById("btnNext").className += " show";
alert("\u8fc7\u5173\uff01\u4e0b\u4e00\u5173\u7684\u5165\u53e3\u5df2\u6253\u5f00")
}})
然后扔到console,回车一下,等待弹出下一关已经打开的提示窗,然后按一下页面中的下一关,继续闯关!
本文写于抑郁的11月中旬,如果冒犯或者损害到原作者的利益或者神马,请邮件告知,第一时间销毁处理。