奋战一天一晚上终于把斗牛游戏给写出来了(主要是除bug时间用的多!若你们发现新的bug欢迎留言)css
这是游戏规则:百度牛牛规则html
点击查看演示:react
我是新手,代码写的有点乱,计算逻辑有点复杂,但最终仍是实现了游戏效果!真的好开心,也深入体会到一点就是,敲代码的时候注意力必定要十分集中,否则后期除bug真的很头痛!其实大部分bug都是一些小错误引发的!算法
做为新生之一,多写点代码,是最能提升本身的能力的!无论要写什么,写多大的程序,把想法、规则、流程先写出来,而后再敲代码,这样才不会乱!
多写代码的最大好处就是能够熟练API的使用,本人还没学任何第三方类库呢,连JQ都没学过,到如今还一直撸原生,暂时是ES5,过段时间再练习ES6,听说,学好原生,再学别的都很容易上手,我一直很相信这句话,就是不知道靠不靠谱,求前辈们指点迷津!很烦恼的一个问题:选NG呢?仍是react呢?数组
HTML、CSS的代码都是用的比较基础的知识,你们应该都能看懂,就很少废话了;app
根据游戏规则(这里我只写了支持4个玩家),比较核心的有几个:建立一副扑克牌、建立玩家、计算出牛几dom
Game
类,下面是代码+注释//=================Game类 function Game() { this.systemCards = Game.createCards(); } //静态方法createCards建立一副牌并返回乱序后的牌 Game.createCards = function(){ var cards = [], cardType = [1, 2, 3, 4], //牌的花色,为了便于比较不一样花色的大小,采用数值代替黑桃、红桃等文字 cardPoint = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; //牌面值,J、Q、K分别为11,12,13,这里没有大小王 cardPoint.forEach(function (p) { //两个forEach循环为每一个花色建立一组牌,共四组,合计52张牌 cardType.forEach(function (t) { var card = {}; card[t] = p; cards.push(card) }) }); return this.disorder(cards); }; //乱序方法,互相替换位置i的元素和随机位置的元素(中间变量temp保存值),这是很基础的用法,必须掌握 Game.disorder = function (cards){ var temp,len = cards.length; for(var i = 0; i < len; i++){ var r = getRandom(0,len); temp = cards[i]; cards[i] = cards[r]; cards[r] = temp; } return cards; };
Player
//=================Player类 var Player = function () { this.banker = false; //是否为庄家 this.money = 1000; }; //返回一副牌中的前5张牌,后期用于发牌给玩家 Player.prototype.getCards = function (g) { g.systemCards = g.systemCards.length < 20 ? Game.createCards() : g.systemCards;//牌不够发时从新拿一副牌,4我的*5=20 return g.systemCards.splice(0,5); };
//计算核心函数`sufuCaculate`,这里扩展了`Array`类,只是为了方便调用,前提是得取个个性一点的名字,否则哪一天官方也出个同名的内置方法,你的项目就悲催了! Array.prototype.sufuCaculate = function () { if((!this)||this.length<5){throw new Error('sufuCaculate()参数错误')} var card5 = this, before10Count = 0, after10Count = 0, maxSameCount = getMaxSameCount(this); if(maxSameCount == 4){return 14} //4张相同的,返回'炸弹' if(this.every(function (x) {return x<5;})){return 13}//5张牌都小于5,返回'五小' if(this.every(function (x) {return x>10;})){return 12}//5张牌全为花,返回'五花' turnTrueValue(card5);//把大于10的牌变成10,并计算等于10的牌的数量 if(before10Count == 1 && after10Count == 5){return 11}//5张牌中一张为10,另外4张为花,返回'四花' //用了三层的for循环才实现了,求前辈们指点好一点的逻辑! for(var i = 0; i<3; i++){ for(var j = i+1;j<4;j++){ for(var k = j+1;k<5;k++){ if(sum([card5[i],card5[j],card5[k]]) == 0){ var copy = card5.slice(); delete copy[i]; delete copy[j]; delete copy[k]; var a = sum(copy.filter(function(){return true})); if(a == 0){return 10}//牛牛 else{return a}//a牛 } } } } return 0;//'无牛' //求和并求于10 function sum(arr){ return arr.reduce(function(a,b){return a+b})%10 } //得到最大相同牌数 function getMaxSameCount(card5){ var count = 1, sameCardCount = 1; for(var i = 0; i<5; i++){ for(var j = 0; j<5;j++){ if(j == i){continue} if(card5.indexOf(card5[i],j)>0){ count++; } } sameCardCount = Math.max(count,sameCardCount); count = 1; } return sameCardCount; } //把大于10的牌所有转为10 function turnTrueValue(card5){ for(var i = 0; i<5; i++){ if(card5[i] === 10){before10Count++} card5[i] = card5[i]>=10 ? 10 : card5[i]; if(card5[i] === 10){after10Count++} } } };
接下来,怎么写呢?本人是这样的,先从程序入口出发,一步一步往前推,写到感受会被重复用到的代码就拿出来,放进一个单独的工具函数,方便重复使用,能够大大地减小代码量!注意工具函数放的位置,若是它只被用在某函数内部,且依赖该函数的变量的话就绝不客气的放在这个函数里面吧,省的传参等的麻烦,要是它会被大于2个函数使用,就放在外面吧,把参数设置成通用的。ide
游戏入口函数start()
,游戏要开始,得有个按钮按吧,好吧来个按钮绑定事件beginBtn.onclick
,点击后进入游戏界面(显示基本元素)函数
//点击开始按钮执行 beginBtn.onclick = function () { var str = prompt('请输入游戏名字','玩家'); player3Name.innerHTML = (str||'玩家')+' 金钱:'; begin(); firstBanker(); showOrHide('none',beginBtn); showOrHide('block',player1Money,player2Money,player3Money,player4Money,player3Bg); showOrHide.bind(null,'block').apply(null,playerCards); //这是我为了偷懒,突发奇想写出来的,竟然能用,呵呵了!若是看不懂,就去苏福的博客园看bind,apply的文章 showOrHide.bind(null,'block').apply(null,playerNameMoneys); runing(); };
开始后得有玩家吧,好吧,建立4个玩家,放进一个数组里面,方便访问工具
//建立四个玩家 function begin(){ for(var i = 0; i<4 ; i++){ players.push(new Player()); } }
第一轮先随机肯定庄家firstBanker()
,不介绍了,本身看源码。
而后呢,得来个倒计时函数 runTime(msg,t)
,这个函数比较通用,在start()范围内会被重复使用,设计以下:
//倒计时函数,看不懂?多翻翻个人博客,有相关知识介绍的文章 function runTime(msg,t){ t--; if(t<0){clearTimeout(timeId);timeId=null;return} info.innerHTML = msg; timeInfo.innerHTML = t; timeId = setTimeout(function () { runTime(msg,t); },1000); }
在点击开始按钮时,会启动游戏的流程,事件回调函数内调用runing()
function runing(){ runTime('请押注',4); callIn(); //押注函数 waiting(deal,5);//等待delay时间到时,执行deal发牌方法 }
是的,须要个押注函数callIn()
,补贴代码了,比较简单,电脑玩家给个随机数,主角经过按钮来取值,而庄家不取值(其它代码就是界面元素的显示与隐藏)
接下来就是deal()
方法,发牌方法,取出玩家的手牌的数据,把花色值、牌面值、牛几分别存进单独的数组,以便使用。
function deal() { for(var i = 0; i<4 ; i++){ cs[i] = players[i].getCards(g); keys[i] = getKeyValue(cs[i]).keys; values[i] = getKeyValue(cs[i]).values; results[i] = getCow(values[i]) } showOrHide('none',player3InBtn); setCard('set',keys,values,player1Card,player2Card,player3Card,player4Card); runTime('计算结果',4); waiting(function(){showResult(results)},5); }
上面这个deal()
方法又须要调用显示或隐藏元素的showOrHide()
方法,看名字就知道啦,因此函数的命名很关键,否则代码长了,本身都不知道这是什么鬼了!showOrHide()
这个方法用的最多,因此必须写成通用的格式:
//改变元素的display属性,看不懂?多翻翻个人博客,有相关知识介绍的文章 function showOrHide(str){ var len = arguments.length; for(var i=1;i<len;i++){ arguments[i].style.display = str; } }
这里先贴个工具函数:
//得到某一张牌的类型和值`getKeyValue()`,用来取牌的花色值和牌面值,内置的`Object.keys()`方法不支持IE9如下 var getKeyValue = function (cards){ var keys = [],values = []; cards.forEach(function (n) { var key = +Object.keys(n); keys.push(key); values.push(n[key]); }); return { keys:keys, values:values }; };
发牌函数里调用了setCard()
,显示或重置全部玩家的牌,我写的这个斗牛游戏的牌不是用的图片,而是用了CSS,下面的changeClassName()
就是用来应用样式的函数
//显示全部玩家的牌 function setCard(str,ks,vs){ //str='set' or 'reset' var args = Array.prototype.slice.call(arguments,3),len = args.length; for(var j=0;j<len;j++){ var n = args[j].id.charAt(6)-1; for(var i = 0;i<5;i++){ if(str === 'reset'){ changeClassName(args[j].children[i],'card-'+ks[n][i],false); args[j].children[i].innerHTML = ''; }else if(str === 'set'){ changeClassName(args[j].children[i],'card-'+ks[n][i],true); args[j].children[i].innerHTML = vs[n][i]; } } } }
玩家压完注后,得显示结果了,showResult()
,这里又用到了showOrHide()
、runTime()
、waiting()
//显示全部玩家的结果 function showResult(results){ var resultsStr = []; for(var i=0;i<4;i++){ resultsStr.push(getResultInfo(results[i])); } showOrHide('block',result1,result2,result3,result4); result1.innerHTML = resultsStr[0]; result2.innerHTML = resultsStr[1]; result3.innerHTML = resultsStr[2]; result4.innerHTML = resultsStr[3]; var str = pay(); //pay()就是付钱的意思啦 runTime(str,10); waiting(function(){showOrHide('block',restartBtn);},11) //延时时间到后显示下一局按钮 }
第一局完了,要开始下一局了(之后的全部下一局都将同样),restartBtn.onclick = restart;
,好吧,定义个回调函数restart()
function restart(){ showOrHide('none',restartBtn); resetBanker(); //根据上一局结果,根据游戏规则,重选庄家 setCard('reset',keys,values,player1Card,player2Card,player3Card,player4Card); clearArrays(results,cs,keys,values,playerCallIns); //清楚上一局保存的全部数据 showOrHide('none',result1,result2,result3,result4); runing(); //循环开始了!!! }
就这样,游戏就能够一直玩下去了,一直能够点下一局!