不得不說,做游戲是會上癮的,這次帶來的是win系統上的經典游戲《蜘蛛紙牌》,不能完美,但求一玩
DEMO:
http://colorgamer.com/demo/webgame/spider/
關於蜘蛛紙牌
規則請打開win系統的蜘蛛紙牌,然后點擊幫助
這里要實現的
- 同樣是兩副牌,一共104張
- 同一種花色的低難度游戲
需要解決的問題
- 1、洗牌
- 2、判斷點擊牌所在序列是否符合可移動條件
- 3、判斷目標位置是否符合可移動條件
- 4、移動符合條件的紙牌序列到目標位置
- 5、完成一個完整序列時的清除
- 6、發牌
綜合起來,《蜘蛛紙牌》基本上就這么6個問題,解決了,也就完成了。下面一個一個來實現
1、洗牌
光洗牌,不難。如果要做到每次洗牌都有解就不是我能解決的問題了(win系統里的蜘蛛紙牌是不是每次都有解,我確實不知道)。這里就隨便洗洗,沒解也沒辦法,即使有解,你也不一定能解完,是吧。
這里是同色,所以不用去考慮4個花色,那一副牌,從A-K,是13張,有4組,就是13*4=52張牌。兩副就是104張。先初始化一組牌
var _cards_init=['1','2','3','4','5','6','7','8','9','10','J','Q','K'];
跟着把它變成1副牌
_cards_init=_cards_init.concat(_cards_init,_cards_init,_cards_init); //使用concat來鏈接數組
這里自己鏈接自己3次,就變成了4組,一副牌。再接着鏈接自己一次,就變成兩副牌
_cards_init=_cards_init.concat(_cards_init);
兩副牌有了,接着就洗牌,這里簡單的使用隨機數來洗。
//洗牌 function shuffle(){ var len=_cards_init.length; if(len>0){ var i=random(len); _cards.push(_cards_init[i]); _cards_init.splice(i,1); shuffle(); } }
這是一個遞歸,每次從初始化的剩余牌序列里隨機取一個,放到新牌的數組里,然后將取走的牌從初始化的數組里移除,重計長度,只要長度不小於1,那么重復這個操作,至到全部取完為止。這樣生成的新牌數組里的牌基本上就是亂序了,達到洗牌的效果。
牌洗好之后,就得將最開始的牌面放到界面上。win7的蜘蛛紙牌是10列,前4列5張扣牌,1張明牌,后6列4張扣牌,1張明牌。這里也按這樣的方式開始。
因為是按列為基礎進行擺牌,所以程序在確定牌所在位置的時候,需要分別記錄,每一列等於是一個序列,有牌移進來,要增加它的長度,有牌移走的時候,要減少它的長度。所以這里使用了一個數組來分別記錄牌的信息
var _cards_table=new Array();
接着確定位置,按照初始化的界面,一共要放54張牌到牌面上,其中44張是扣牌,10張是明牌,所以,先從新牌數組里取54張牌出來,其中前10張,也就是0-9,是個位數,直接將下標做了牌的參數
for(var i=0;i<10;i++){ _cards_table[i]=new Array(); } var _html_cards_table=''; for(var i=0;i<54;i++){ if(i<=9){ j=i; _html_cards_table+="<div class='cardbg cardbg"+j+"' action-t='' action-line='"+j+"'></div>"; }else{ var s=i.toString(); j=s.substring(s.length-1); } j=parseInt(j); var num=cardNum(_cards[i]); var back=''; //背面牌 if(i>=0&&i<44){ back='back'; } var l=_cards_table[j].length; _html_cards_table+="<div class='card cardv"+num+" card"+j+" "+back+"' action-t='"+back+"' action-line='"+j+"' action-index='"+l+"' style='z-index:1;position:absolute;left:"+_cardsPostion[0]+"px;top:"+_cardsPostion[1]+"px;'>"+_cards[i]+"</div>"; _cards_table[j].push(num); }
其實,這里固定牌的位置,直接使用了CSS樣式,不太好,因為有時候你不知道到底一列會出現多少個紙牌,要是CSS樣式寫少了,那多出來的紙牌放的位置就會出現問題。最好的辦法是JS直接判斷索引值,根據索引值*固定數來確定位置。
這些牌放出去后,自然得將它們從新牌的數組里移除,以后發牌,就是數組里剩下的牌了
_cards.splice(0,54); _cards_table+='<div id="post">發牌<span id="postnum">5</span></div>'; //順便添加一個發牌的按鈕 $("#game_map").html(_cards_table); //將生成的牌放到游戲地圖上
這里除了這些初始牌面外,還有一個東西,那就是隱藏的一個牌,因為每列的牌都有可能完全的移出,這時候符合條件的牌序列又可以移到這個空列里來,所以,得在那個位置放一個看不見的牌,但這個牌只有列號,沒有其它的東西。
if(i<=9){ j=i; _cards_table+="<div class='cardbg cardbg"+j+"' action-t='' action-line='"+j+"'></div>"; }
2、判斷點擊牌所在序列是否符合可移動條件。3、判斷目標位置是否符合可移動條件
首先,得為各牌添加單擊事件。然后第一次點的牌要記錄下它的3個東西:1、所在列。2、列上的索引值。3、牌面值(A-K)。再次點擊其它牌的時候,進行判斷,它是不是符合移動條件,如:被點擊牌及之后的牌序列是不是符合條件(遞減),目標牌是不是比第一次點擊牌大1,如果是,移動牌到新位置,如果不是,給出提示,不符合條件
function clickCard(){ $(".card").unbind('click').click(function(){ var line=$(this).attr('action-line'); var back=$(this).attr('action-t'); if(back){ return false; } var index=parseInt($(this).attr('action-index')); var cards_num; line=parseInt(line); cards_num=_cards_table[line][index]; if(_cards_selected.length==1){ if((cards_num-1)==_cards_selected[0][2]){ var _index=index+1; moveCard(line,_index,_cards_selected[0][2]); }else{ alert('不能移動到那個位置2_'+cards_num+'_'+_cards_selected[0][2]); } css_remove(); _cards_selected.shift(); clickCard(); }else{ if(getCards(line,index)==0){ _cards_selected[0]=[]; _cards_selected[0][0]=line; _cards_selected[0][1]=index; _cards_selected[0][2]=cards_num; }else{ css_remove(); } } }); }
4、移動符合條件的紙牌序列到目標位置
移走了,也就得把它從該列的數組里清除掉,這里使用splice(),只需要傳入起始索引值,將它和它之后的所有牌都清除。然后創建新的牌加入到目標列的數組里
function moveCard(line,index,cards_num){ line=parseInt(line); var len=_cards_table[_cards_selected[0][0]].length; _cards_table[_cards_selected[0][0]].splice(_cards_selected[0][1],len); var html=''; var moveCardIndex=index; _moveCardNum=len-_cards_selected[0][1]; for(var i=_cards_selected[0][1];i<len;i++){ var v=$(".card"+_cards_selected[0][0]).eq(_cards_selected[0][1]).html(); var o=$(".card"+_cards_selected[0][0]).eq(_cards_selected[0][1]); var num=cardNum(o.html()); _cards_table[line].push(num); var cardline=o.attr('action-line'); o.removeClass('card'+cardline); o.addClass('card'+line); o.attr('action-line',line); o.attr('action-index',moveCardIndex); move_animation(o,line,moveCardIndex,1); moveCardIndex++; } $(".card"+_cards_selected[0][0]).eq(_cards_selected[0][1]-1).removeClass('back'); $(".card"+_cards_selected[0][0]).eq(_cards_selected[0][1]-1).attr('action-t',''); $("#scores").html(parseInt($("#scores").html())+1); }
checkComplate(line);用於判斷是否已經實現A-K,如果是將它清除
5、完成一個完整序列時的清除
首先判斷該列的紙牌數是否超過13張,因為如果連13張都沒有,又怎么會有完整的A-K呢。之后再判斷明牌是不是有13張,而且明顯的大小是不是從大到小減1的。如果都符合,那將它們全部清除,並從該列的數組里將它們清除。
function checkComplate(line){ var obj=$(".card"+line) var len=obj.length; var preNum=0; var error=0; var comNum=[]; if(len>=13){ for(var i=0;i<len;i++){ if(obj.eq(i).attr('action-t')==''){ if(preNum>0){ if(parseInt(cardNum(obj.eq(i).html()))+1==preNum){ }else{ comNum=[]; } comNum.push(i); }else{ comNum.push(i); } preNum=parseInt(cardNum(obj.eq(i).html())); } } if(comNum.length>=13){ for(var i in comNum){ obj.eq(comNum[i]).remove(); if(i==0){ obj.eq(comNum[i]-1).removeClass('back'); obj.eq(comNum[i]-1).attr('action-t',''); var len=_cards_table[line].length; _cards_table[line].splice(comNum[i],len); } } _left_group--; if(_left_group==0){ alert('你贏了,共移動'+$('#scores').html()+'次'); $("#re").click(); } } } }
6、發牌
發牌其實和初始化牌面沒什么區別,只是每次只發10張,也就是向0-9列分別增加一張牌,發出去后,剩於牌就減去這些牌
function post(){ $("#post").click(function(){ if(_cards.length<=0){ alert('無牌可發了'); return false; } var _html_cards_table=''; for(var i=0;i<10;i++){; var num=cardNum(_cards[i]); var l=_cards_table[i].length; _html_cards_table+="<div class='card cardv"+num+" card"+i+"' action-t='' action-index='"+l+"' action-line='"+i+"' style='z-index:1;position:absolute;left:"+_cardsPostion[0]+"px;top:"+_cardsPostion[1]+"px;'>"+_cards[i]+"</div>"; _cards_table[i].push(num); } _cards.splice(0,10); var cardbenum=$(".card").length; $("#game_map").append(_html_cards_table); var _i=cardbenum; var _this=this; this.move_animation=function(){ var line=$(".card").eq(_i).attr('action-line'); var index=$(".card").eq(_i).attr('action-index'); move_animation($(".card").eq(_i),line,index); _i++; if(_i<cardbenum+10){ setTimeout(_this.move_animation,100); } } this.move_animation(); $("#postnum").html(parseInt($("#postnum").html())-1); clickCard(); }); }
以上6點,就是《蜘蛛紙牌》的基本內容,代碼里還有一些方法,是為了增加一些方便,比如返回J-K的值等等,這里就不說了。直接編輯源代碼,還是很累的。
里面還有關於動畫的內容,暫時就不講了,全部代碼也不帖了,可以查看試玩頁面的源文件