开源项目 —— 原生JS实现斗地主游戏 ——代码极少、功能都有、直接粘贴即用
目录
效果如下
目录结构
GameEntity.js
GrawGame.js
konva.min.js
PlayGame.js
veriable.js
index.html
结语:
前期回顾
卡通形象人物2 写代码-睡觉 丝滑如德芙_0.活在风浪里的博客-CSDN博客本文实现了包含形象的卡通小人吃、睡、电脑工作的网页动画https://blog.csdn.net/m0_57904695/article/details/128981376?spm=1001.2014.3001.5501
本文实现了包含形象的斗地主网页游戏,js循环动画,简单生动的画面设计。非常丝滑有意思,欢迎对此代码感兴趣的朋友前来下载参考。
如果大家非常好奇该段代码所带来的网页游戏效果的话,那就直接拷贝到自己电脑上,不上传了资源了,方便大家直接玩乐,如果对你有些微帮助还请收藏以备下次及时找到!
本文直接复制可以用,
效果如下
目录结构
GameEntity.js
/*** 实体类;有些实体其中的行为,* 可能没有严格符合面向对象规范,* 原因在于对象与过程之间的界限难以区分*/// 玩家
class Player{constructor(BoardList, name, color){this.Color = color// Board对象this.BoardList = BoardListthis.name = namethis.GroupBoard = null}// 通过ID获取牌GetBoardOfID(id){if(this.BoardList.length>0){for(let i=0;i<this.BoardList.length;i++){if(this.BoardList[i].id == id)return i}}return -1}// 根据ID删除节点DeleteIDOfBoard(id){if(this.BoardList.length<1) returnlet index_ = this.GetBoardOfID(id)this.BoardList.splice(index_, 1)}// 将手中的牌按照顺序排列BoardListSort(){BoardSort(this.BoardList)}// 提示出牌AutoSendBoard(){// 提示出牌需要满足几个条件,// 首先上家没有出牌时,那么按照最大的类型出牌(炸弹除外),如Y2>Y1// 如果上家有出牌,那么需要判断当前牌组之中是否有相对应类型的牌// 玩家需要自己维护自己所有的牌总量(机器人由程序维护),如一手牌组当中有几个飞机几个顺子}// 将当前的牌聚类为一个个的牌组合;返回牌组合数组// 如:987654333 return: Y5,Y3,Y2ClusterType(){}
}// 牌对象
// type ♥,♠,♦,♣
class Board{constructor(surface, type, id){this.surface = surfacethis.type = typethis.id = type + idthis.DrawBoardFront = CreateBorad(width/2-30, height/2-45, this)this.DrawBoardFront._id = id// id必须是要唯一的this.DrawBoardBack = BoardBack(width/2-30, height/2-45, this)this.DrawBoardBack._id = '0'+id}}// 组合牌
class BoardCombin{constructor(BoardList, Type, Value, length){this.BoardList = BoardListthis.Type = Typethis.Value = Valuethis.Length = length}
}// 这里可以优化,使用有限状态机会更好,逻辑更清晰
// 判定出牌边界,类型一致,牌的数量一致,不小于桌面价值量
// 单: 1; Y1
// 对: 2; Y2 *
// 三带: 3~5; Y3
// 顺: 5~12; Y4
// 连对: 6~16; Y5 *
// 飞机: 6~16; Y6
// 四带二: 6~8; Y7 *
// 炸弹: 4 Y8 *
// 王炸: 2 Y8 *
// 牌组分类器
class BoardType{constructor(BoardList, Boards){this.Boards = BoardListthis.BoardList = new Array()if(Boards!=null) this.BoardList = Boards// 将牌对象划为简单的字面量this.BoardListValue = new Array();for(let i=0;i<BoardList.length;i++){this.BoardList.push( BoardList[i].surface )this.BoardListValue.push(BoardMarkMap.get(BoardList[i].surface))}}// 获取出牌的类型,判定牌是否严格属于哪一类型GetType(){var length = this.BoardList.length;// 单牌if(length === 1){return this.FiltrateSign(this.BoardList)}// 对子,王炸if(length === 2){return this.FiltrateTow(this.BoardList)}// 三带,炸弹if(length>=3 && length<=5){return this.FiltrateThree(this.BoardList)}// 飞机,连对,顺子,四带二if(length>=5 && length<=16){return this.FiltrateLine(this.BoardList)}}// 单牌过滤FiltrateSign(BoardList_){var value_ = BoardMarkMap.get(BoardList_[0])return new BoardCombin(this.Boards, "Y1", value_, 1)}// 双牌过滤=》王炸FiltrateTow(BoardList_){if(BoardList_[0]===BoardList_[1]){var value_ = BoardMarkMap.get(BoardList_[0])return new BoardCombin(this.Boards, "Y2", value_, 2)} else{return this.FiltrateKingMax(BoardList_)}}// 三带过滤=》顺子=》炸弹FiltrateThree(BoardList_){var temp = BoardList_.join('')// 其中任一一张牌出现三次以上var reg = /(\d|J|Q|K|A)\1{2}/var index = temp.search(reg)if(!reg.test(temp)) {// 如果没有匹配到三带,那么有可能是顺子if(temp.length===5) return this.FiltrateLine(BoardList_)return null};var value_ = BoardMarkMap.get(BoardList_[index])// 判断是三不带|三带一|三带对temp = temp.replace(reg, '')if(temp.length==0)return new BoardCombin(this.Boards, "Y3", value_, 3)if(temp.length==1 && temp!=BoardList_[index])return new BoardCombin(this.Boards, "Y3", value_, 4)else if(temp.length==1){return this.FiltrateBomb(BoardList_);}if(temp.length==2 && temp[0] == temp[1])return new BoardCombin(this.Boards, "Y3", value_, 5)return null}// 顺子过滤=》连对=》飞机=》四带二FiltrateLine(BoardList_){var temp = BoardList_.join('')// 如果牌组数量大于5,那么更有可能是连对或者飞机,四带二if(temp.length>5){var tempData = null;// 过滤连对,过滤四带二,只有偶数才可if(temp.length%2===0){tempData = this.FiltrateLineTwo(BoardList_)if(tempData != null) return tempDatavar tempData = this.FiltrateFour(BoardList_)if(tempData != null) return tempData}// 飞机过滤tempData = this.FiltrateAir(BoardList_)if(tempData != null) return tempData}// 如果出现2,小鬼,大鬼那么就不是顺子var reg = /(2|C|G)/if(reg.test(temp)) return null;var value_ = this.BoardListValue[0]for(var i=1; i<BoardList_.length; i++){// 顺子必须是连续的,即每个数之间相差要等于1if(this.BoardListValue[i-1]-this.BoardListValue[i]!=1)return null;}return new BoardCombin(this.Boards,'Y4', value_, BoardList_.length)}// 飞机过滤// 飞机可带两张单牌,或者两个对子,亦或者不带FiltrateAir(BoardList_){var temp = BoardList_.join('')// 其中任多张牌出现三次var reg = /(0|[3-9]|J|Q|K|A)\1{2}/g // 三带var tempList_1 = temp.match(reg)if(tempList_1==null) return nullvar recode = 0// 飞机至少要是两个连起来的三带for(var i=1; i<tempList_1.length; i++){var i1 = BoardMarkMap.get(tempList_1[i][0])var i2 = BoardMarkMap.get(tempList_1[i-1][0])if(i2-i1==1){temp = temp.replace(tempList_1[i],'')temp = temp.replace(tempList_1[i-1],'')}elserecode++}var len = tempList_1.length-recodeif(len<2) return null// 返回牌组对象var value_ = BoardMarkMap.get(tempList_1[0][0])// 三不带if(temp.length===0)return new BoardCombin(this.Boards, 'Y6', value_, BoardList_.length)// 判断剩余的牌,剩余的牌有可能是单牌也可能是对子var reg_Two = /(\d|J|Q|K|A|2)\1{1}/g // 对子var tempList_2 = temp.match(reg_Two)// 飞机带对子,每个飞机可以带一个对子所以必须数量一致if(tempList_2!=null && tempList_2.length!=len && temp.length!=len)return nullif(tempList_2==null && temp.length!=len)return nullreturn new BoardCombin(this.Boards, 'Y6', value_, BoardList_.length)}// 四带二,四可带两张or两对FiltrateFour(BoardList_){if(BoardList_.length>8) return null// 因为四带二是由炸弹组成,匹配四张牌var temp = BoardList_.join('')// 其中任一一张牌出现四次var reg = /(\d|J|Q|K|A)\1{3}/var index = temp.search(reg)if(index==-1) return null// 将四张一样的牌剔除掉temp = temp.replace(reg,'')var reg_Two = /(\d|J|Q|K|A|C|G)\1{1}/g// 匹配剩余的牌;var temp_list2 = temp.match(reg_Two)if(temp.length==4 && temp_list2.length!=2) return null if(temp.length==2 && temp_list2.length==0)return null// 获取四带二的价值面var value_ = BoardMarkMap.get(BoardList_[index])return new BoardCombin(this.Boards, 'Y7', value_, BoardList_.length)}// 连对FiltrateLineTwo(BoardList_){var temp = BoardList_.join('')// 连对边缘判断,包含2,小鬼,大鬼就不是连对,且连对为偶数if((/(2|C|G)/).test(temp) || temp.length%2!=0) return null;var reg = /(\d|J|Q|K)\1{1}/gif(temp.replace(reg,'')!=='')return null;var tempList = temp.match(reg)if(tempList.length>=3){// 判断连续的对子是否为顺序的for(let j=1; j<tempList.length; j++){var tempData_1 = BoardMarkMap.get(tempList[j][0])var tempData_2 = BoardMarkMap.get(tempList[j-1][0])if(tempData_2-tempData_1!==1)return null;}}var value_ = BoardMarkMap.get(tempList[0][0])return new BoardCombin(this.Boards, 'Y5', value_, BoardList_.length)}// 炸弹FiltrateBomb(BoardList_){var temp = BoardList_.join('')// 其中任一一张牌出现四次var reg = /(\d|J|Q|K)\1{3}/if(!reg.test(temp)) return nullvar value_1 = BoardMarkMap.get(BoardList_[0])return new BoardCombin(this.Boards, 'Y8', value_1, 4)}// 王炸FiltrateKingMax(BoardList_){if(BoardList_[0]==="G" && BoardList_[1]==="C"){var value_1 = BoardMarkMap.get(BoardList_[0])var value_2 = BoardMarkMap.get(BoardList_[1])return new BoardCombin(this.Boards, "Y8", value_1+value_2, 2)}return null}// 将牌变为所有该类型GetAllType(type, value_){switch(type){case 'Y1':return this.FiltrateSign_All(value_)}}// 返回最小的一张单牌FiltrateSign_All(value_){for(let i=this.BoardListValue.length; i>0; i--){if(this.BoardListValue[i-1]>value_)return [this.Boards[i-1]]}return null}}// 玩家出牌记录器
class BoardPlayer{constructor(BoardList, PlayerIndex){if(BoardList instanceof Map)BoardList = Array.from(BoardList.values())// 先将牌面进行排序BoardSort(BoardList)let Boards = new BoardType(BoardList)// 再将牌变为合法的规则牌this.CurrentBoardCombin = Boards.GetType()this.CurrentBoards = Boards.Boardsthis.PlayerIndex = PlayerIndex}Show(){let typeShow = this.CurrentBoardCombinif(typeShow==null) {TxtInfo.innerText = '无效牌'return null}TxtInfo.innerText = BoardTypeMap.get(typeShow.Type)+'-'+typeShow.Valuereturn typeShow}
}// 已出牌池
class BoardPond{constructor(){this.CurrentBoardPond = new Array()}// 出牌,将牌置入出牌池SetAddBoard(BoardPlayer){this.CurrentBoardPond.unshift(BoardPlayer)}// 获取牌顶的类型GetCurrentBoard(){return this.CurrentBoardPond[0].CurrentBoardCombin}
}
GrawGame.js
/*** 游戏的画面及动作绘制*///------------------- 绘制各种图形和动作// 绘制牌
function CreateBorad(x, y, Board){var group = new Konva.Group()var text = Board.surfacevar color = 'black'var width_ = 25var x_ = x+5if(text=='G'){color = 'red'}if(text=='C'||text=='G') {width_ = 14text='Joke'}if(text=='0') {text = '10' x_ = x+8}var type = Board.typevar rect = new Konva.Rect({x: x,y: y,stroke: '#555',fill: 'white',strokeWidth: 1,shadowColor: 'black',shadowBlur: 0,shadowOffset: { x: 5, y: 5 },shadowOpacity: 0.3,width: BoardWidth,height: BoardHight,});group.add(rect)if(type=='♠' || type=='♣') color = 'black'else if(type=='♥' || type=='♦') color = 'red'var BoardTxt = new Konva.Text({x: x+5,y: y+5,text: text,fontSize: 18,width: width_,fill: color});group.add(BoardTxt)if(text!='Joke'){group.add(new Konva.Text({x: x_,y: y+20,text: type,fontSize: 20,fill: color}))group.add(new Konva.Text({x: x+BoardWidth*0.33,y: y+25,text: type,fontSize: BoardWidth*0.666+5,fill: color}))}else{// 绘制大符号group.add(new Konva.Text({x: x+BoardWidth*0.266,y: y+30,text: type,fontSize: 25,fill: color}))}return group;
}// 选中牌动画绘制;IsPass:是否绕过该判断,直接对牌进行操作,一般用于牌的初始化
function GoOrOut(node, Board, IsPass=false){// console.log(node)if(!IsLockClick && !IsPass) returnlet id = 0let BoardNode = null// 有可能是直接操作的对象if(node.target){id = node.target.parent._idBoardNode = node.target.parent}else{id = node._idBoardNode = node}let tempI = 20if(!TempBorad.has(id)){TempBorad.set(id, Board)}else{tempI = -20TempBorad.delete(id)}var tween = new Konva.Tween({node: BoardNode,duration: 0.005,y: BoardNode.attrs.y-tempI,});tween.play();
}// 取消选中的牌,将牌归位
function RestoreBoard(){return new Promise((a, b)=>{IsLockClick = truelet TempBorad_ = Array.from(TempBorad)for(let i=0;i<TempBorad_.length;i++){// console.log(TempBorad_[i])GoOrOut(TempBorad_[i][1].DrawBoardFront, TempBorad_[i][1], true)}IsLockClick = falsea()})}// 绘制牌的反面
function BoardBack(x, y){var group = new Konva.Group()var rect = new Konva.Rect({x: x,y: y,stroke: '#555',fill: 'white',strokeWidth: 1,shadowColor: 'black',shadowBlur: 0,shadowOffset: { x: 5, y: 5 },shadowOpacity: 0.3,width: BoardWidth,height: BoardHight,})group.add(rect)for(var i=0; i<10; i++){let tempPoints = new Array()for(var j=0; j<10; j++){if(j%2==0){tempPoints.push(x+ i*BoardWidth/10+BoardWidth/10)}else{tempPoints.push(x+ i*BoardWidth/10)}tempPoints.push(y+BoardHight/9*j)}var redLine = new Konva.Line({points: tempPoints,stroke: 'block',strokeWidth: 1,lineCap: 'round',lineJoin: 'round'});group.add(redLine)}return group
}// 旋转牌并移动角度并移动位置
// 动画执行到百分之多少就开始停止阻塞
function RotateBoard(node, rotation, duration, x, y, stopblock){return new Promise((a, b)=>{if(stopblock==null||stopblock==undefined||stopblock>1) stopblock = 1if(stopblock<0) stopblock = 0var temp = CalcXAndY(x, y, node.children[0].attrs.x, node.children[0].attrs.y)let oldX = temp.xlet oldY = temp.yvar tween = new Konva.Tween({node: node,duration: duration,x: oldX,y: oldY,rotation: rotation});tween.play();setTimeout(() => {a()}, duration*1000*stopblock);})
}// 绘制倒计时的秒表
function DrawTime(){var simpleText = new Konva.Text({x: width / 2 - 40,y: height / 2 - 50,id: 'Time',text: EveryTime,fontSize: 40,fontFamily: 'Calibri',stroke: 'black',fill: 'white',padding: 5,align: 'center'});return simpleText
}// 绘制地主和农民的标签
function ClassTitle(){let x1 = width*0.05+3let y1 = 5let x2 = width*0.95 - BoardWidth +3let y2 = 5let myx = (width)/2 - 18 let myy = height-20BoardLayer.add(Draw(x1, y1, Loandlords_Paly_==1?'地主':'农民', Loandlords_Paly_==1?'blue':'black', 'white'))BoardLayer.add(Draw(x2, y2, Loandlords_Paly_==2?'地主':'农民', Loandlords_Paly_==2?'blue':'black', 'white'))BoardLayer.add(Draw(myx, myy, Loandlords_Paly_==0?'地主':'农民', Loandlords_Paly_==0?'blue':'black', 'white'))function Draw( x, y, text, bgcolor, txtcolor){var tooltip = new Konva.Label({x: x,y: y,opacity: 0.75});tooltip.add(new Konva.Tag({fill: bgcolor,lineJoin: 'round',shadowColor: 'black',shadowBlur: 10,shadowOffsetX: 10,shadowOffsetY: 10,shadowOpacity: 0.5}));tooltip.add(new Konva.Text({text: text,fontFamily: 'Calibri',fontSize: 15,padding: 3,fill: txtcolor}));return tooltip}
}// 绘制桌子
function DrawTable(){var play0Rect = new Konva.Rect({x: (width/2)-(((20-1)*25+BoardWidth)/2)-10,y: (height)-height*0.05-BoardHight - 10,width: ((20-1)*25+BoardWidth)+20,height: BoardHight + 20,cornerRadius: [10, 10, 10, 10],fill: PlayerList[0].Color,});var play1Rect = new Konva.Rect({x: 30,y: 20,width: BoardWidth+30,height: 18*17,cornerRadius: [10, 10, 10, 10],fill: PlayerList[1].Color});var play2Rect = new Konva.Rect({x: width - (BoardWidth+60),y: 20,width: BoardWidth+30,height: 18*17,cornerRadius: [10, 10, 10, 10],fill: PlayerList[2].Color,});Tab_ = new Konva.Rect({x: ((width/2) - 600/2),y: ((height/2) - 200/2 - 40),width: 600,height: 200,cornerRadius: [10, 10, 10, 10],fill: 'DarkGreen',});BoardLayer.add(play0Rect);BoardLayer.add(play1Rect);BoardLayer.add(play2Rect);BoardLayer.add(Tab_);
}// 绘制桌子颜色
function DrawTabColor(pIndex){var tween = new Konva.Tween({node: Tab_,duration: 1,fill: PlayerList[pIndex].Color});tween.play()
}//-------------------// 计算X和目标X,Y和目标Y相距多少
function CalcXAndY(x1, y1, x2, y2){let tempX = x1-x2let tempY = y1-y2return { x: tempX, y: tempY }
}// 发牌动画
function DrawSendBoard(){return new Promise(async (a, b)=>{// 发牌给玩家var GroupWidth = (width/2)-((16*25+BoardWidth)/2)var BaseY = (height)-height*0.05-BoardHight// 发牌给上家var BaseX_1 = width*0.05var BaseY_1 = BoardHight*0.3// 发牌给下家var BaseX_2 = width*0.95 - BoardWidthvar BaseY_2 = BoardHight*0.3var i = 0for(let i=0; i<17; i++){await RotateBoard(PlayerList[1].BoardList[i].DrawBoardBack, 0, 0.25, BaseX_1, BaseY_1+i*10, 0.05)RotateBoard(PlayerList[1].BoardList[i].DrawBoardFront, 0, 0.25, BaseX_1, BaseY_1+i*10, 0)RotateBoard(PlayerList[2].BoardList[i].DrawBoardBack, 0, 0.25, BaseX_2, BaseY_2+i*10, 0.05) RotateBoard(PlayerList[2].BoardList[i].DrawBoardFront, 0, 0.25, BaseX_2, BaseY_2+i*10, 0) RotateBoard(PlayerList[0].BoardList[i].DrawBoardFront, 0, 0.25, i*25+GroupWidth, BaseY, 0.05)}a()})
}// 将牌整理到合适位置
function InitLocationBoard(player){return new Promise(async (a,b)=>{if(player.name=='玩家一'){player.BoardListSort()IsLockClick = falselet len = player.BoardList.lengthlet width_ = (width/2)-(((len-1)*25+BoardWidth)/2)var BaseY = (height)-height*0.05-BoardHightfor(let i=0; i<player.BoardList.length; i++){player.BoardList[i].DrawBoardFront.remove()let tempDraw = player.BoardList[i].DrawBoardFrontMyBoardGroup.add(tempDraw)if(tempDraw)await RotateBoard(tempDraw, 0, 0.1, i*25+width_, BaseY, 0.2)}}a()})}// 玩家出牌信息绘制区
function PlayerMessage(player, BoardCombin){let Group_ = player.Grouplet Boards = BoardCombin.CurrentBoardslet len = Boards.lengthlet x, y;// 玩家一出牌动画function PalyerMySend(){return new Promise(async (result, reject)=>{Group_.destroyChildren()x = (width/2)-(((len-1)*25+BoardWidth)/2)y = 270-BoardHightfor(let i = 0; i<len; i++){let node = Boards[i].DrawBoardFront// 注销事件node.off('mouseenter.select')node.off('mousedown.click')node.remove()Group_.add(node)await RotateBoard(node, 0, 0.2, i*25+x, y, 0)player.DeleteIDOfBoard(Boards[i].id)}result()})}// 玩家二或三出牌动画function PalyerTwoOrThreeSend(player, BoardCombin){return new Promise(async (result, reject)=>{Group_.destroyChildren()if(player.name=='玩家二')x = ((width/2) - 600/2)elsex = ((width/2) + 600/2 - BoardWidth)y = ((height/2) - 200/2 - 40)for(let i = 0; i<len; i++){let node1 = Boards[i].DrawBoardFrontlet node2 = Boards[i].DrawBoardBacknode1.remove()node2.remove()Group_.add(node1)Group_.add(node2)RotateBoard(node1, 0, 0.2, i*25+x, y, 0)await RotateBoard(node2, 0, 0.2, i*25+x, y, 0)player.DeleteIDOfBoard(Boards[i].id)node1.show()node2.hide()}BoardLayer.draw()result()})}return new Promise(async (result, reject)=>{if(BoardCombin==null){// 画不要result()return}if(player.name=='玩家一'){await PalyerMySend(player, BoardCombin)}else{await PalyerTwoOrThreeSend(player, BoardCombin)}await InitLocationBoard(player)result()})
}
konva.min.js
这里代码比较长,已上传主页资源,也可以评论区找我要~
PlayGame.js
/*** 游戏主体逻辑,包括开始游戏,结束游戏等* 主要是开始游戏到循环游戏直到游戏结束的状态变化* 也包括初始化整个游戏*/// 牌面初始化
function InitBoard(){// 将牌打乱for(let i=53;i>0;i--){BoardListGameInit[i].DrawBoardFront.hide()BoardListGameInit[i].DrawBoardBack.hide()let randomIndex = Math.floor( Math.random()*i+1)BoardListGameInit.push(BoardListGameInit[randomIndex])BoardListGameInit.splice(randomIndex,1)}// 地主牌for(let i=0; i<6; i++){let temp = 3if(i==0) temp-=1PlayerList[0].BoardList.push(...BoardListGameInit.splice(0, temp))PlayerList[1].BoardList.push(...BoardListGameInit.splice(0, temp))PlayerList[2].BoardList.push(...BoardListGameInit.splice(0, temp))}// 将牌排列整齐BoardSort(BoardListGameInit)PlayerList[0].BoardListSort()PlayerList[1].BoardListSort()PlayerList[2].BoardListSort()}// 按钮闭包
var Button_ = (function(){// 注册事件let EventListener = new Array()// 显示按钮且添加事件function ShowButtonAndEvent(text1, text2, buttonLeftEvent, buttonRightEvent){// 添加事件之前需要先注销掉之前的事件HideButtonAndDeEvent()ShowAndHide(true)ButtonOne1.innerText = text1ButtonOne2.innerText = text2ButtonOne1.addEventListener('click',buttonLeftEvent)ButtonOne2.addEventListener('click',buttonRightEvent)EventListener[0] = buttonLeftEventEventListener[1] = buttonRightEvent}// 隐藏按钮且删除事件function HideButtonAndDeEvent(){ShowAndHide(false)// 移除事件if(EventListener.length>0){ButtonOne1.removeEventListener('click',EventListener[0])ButtonOne2.removeEventListener('click',EventListener[1])EventListener.splice(0,1)EventListener.splice(0,1)}}// 隐藏或显示function ShowAndHide(IsShow){ButtonDiv.style.display = IsShow? 'block':'none'}return {ShowButtonAndEvent: ShowButtonAndEvent,HideButtonAndDeEvent: HideButtonAndDeEvent,ShowAndHide: ShowAndHide}
})()// 倒计时闭包
var TimeSecond = (function(){let i = EveryTime// 绘制秒表let timeControl = null// 是否暂停let IsPause = false// 动画IDlet RAFId = nullfunction Show(){if(!timeControl){timeControl = DrawTime()BoardLayer.add(timeControl)}timeControl.show()DrawStart()}let oldTime = new Date()let newDate = 0// 开始倒计时function DrawStart(){newDate = new Date()if(newDate-oldTime > 1000 && !IsPause){// console.log()timeControl.text(i<10?'0'+i:i)BoardLayer.draw()i--oldTime = new Date()}if(i<0){Close()// 默认当该倒计时结束,那么触发第一个按钮的点击事件ButtonOne1.click()return} RAFId = window.requestAnimationFrame(DrawStart)}// 关闭且初始化function Close(){if(RAFId)window.cancelAnimationFrame(RAFId)i = 20IsPause = falsetimeControl.hide()BoardLayer.draw()RAFId = null}return {timeControl: timeControl,Show: Show,Close: Close,IsPause: IsPause}
})()// 发牌
async function SendBorad(){// 玩家牌组// 在玩家牌组上面注册一个组合,如果在该组合上面点击,那么将开启选牌// 离开该组合将注销let IsSelect = false // 是否可以选中牌MyBoardGroup = new Konva.Group()NotMeBoard_Group = new Konva.Group()MyBoardGroup.on('mousedown', ()=> { IsSelect=true })MyBoardGroup.on('mouseup mouseleave', ()=> { IsSelect=false;if(TempBorad!=null)new BoardPlayer(TempBorad, 0).Show()})// 给地主牌注册事件,因为该地主牌有可能是玩家的手牌for(let i=0;i<3; i++){BoardListGameInit[i].DrawBoardFront.show()BoardListGameInit[i].DrawBoardBack.hide()// 注册事件BoardListGameInit[i].DrawBoardFront.on('mouseenter.select', (node)=>{if (IsSelect && Loandlords_Paly_==0)GoOrOut(node, BoardListGameInit[i])})BoardListGameInit[i].DrawBoardFront.on('mousedown.click', (node)=>{if(Loandlords_Paly_==0)GoOrOut(node, BoardListGameInit[i])})}// 生成具体的牌对象for(let i = 0;i<3; i++){for(let j=0; j<PlayerList[i].BoardList.length;j++){let Board = PlayerList[i].BoardList[j]let group = nullif(i==0){group = Board.DrawBoardFrontgroup.on('mouseenter.select', (node)=>{if (IsSelect)GoOrOut(node, Board)})group.on('mousedown.click', (node)=>{GoOrOut(node, Board)})// console.log(group)MyBoardGroup.add(group)}else{group = Board.DrawBoardBacklet DrawBoardFront = Board.DrawBoardFrontDrawBoardFront.hide()NotMeBoard_Group.add(group)NotMeBoard_Group.add(DrawBoardFront)}group.show()}}BoardLayer.add(MyBoardGroup)BoardLayer.add(NotMeBoard_Group)// 开始发牌动画await DrawSendBoard(BoardLayer)StartGame()
}// 开始玩地主
async function StartGame(){// 将地主牌放到左上角for(let i = 0;i<3;i++){BoardLayer.add(BoardListGameInit[i].DrawBoardFront)BoardLayer.add(BoardListGameInit[i].DrawBoardBack)RotateBoard(BoardListGameInit[i].DrawBoardFront, 0, 0.2, width*0.15+i*BoardWidth, 0)RotateBoard(BoardListGameInit[i].DrawBoardBack, 0, 0.2, width*0.15+i*BoardWidth, 0.2)}// 叫地主setTimeout(() => {TimeSecond.Show()Loandlords_Paly()}, 500);// 将决定一个地主出来function Loandlords_Paly(){// 显示按钮且绑定事件Button_.ShowButtonAndEvent('不叫','叫地主', async ()=>{Loandlords_Paly_ = Math.floor( Math.random()*2+1)// 将倒计时关闭TimeSecond.Close()Button_.HideButtonAndDeEvent(true)// 必须等待动画完成await new Promise(async (a,b)=>{for(let i = 0; i<3; i++){BoardListGameInit[i].DrawBoardBack.show()PlayerList[Loandlords_Paly_].BoardList.push(BoardListGameInit[i])NotMeBoard_Group.add(BoardListGameInit[i].DrawBoardBack)if(Loandlords_Paly_==1)await RotateBoard(BoardListGameInit[i].DrawBoardBack, 0, 0.2, width*0.05, (BoardHight*0.3)+(17*10)+i*10)elseawait RotateBoard(BoardListGameInit[i].DrawBoardBack, 0, 0.2, (width*0.95 - BoardWidth), (BoardHight*0.3)+(17*10)+i*10)PlayerList[Loandlords_Paly_].BoardListSort()a()}})StartFunGame()}, async ()=>{// 将倒计时关闭TimeSecond.Close()Button_.HideButtonAndDeEvent(true)Loandlords_Paly_ = 0// 叫地主;那么将地主牌添加到玩家手牌中PlayerList[0].BoardList.push(...BoardListGameInit)// 整理牌await InitLocationBoard(PlayerList[0])StartFunGame()})}
}// 开始游戏循环
function StartFunGame(){// 实例化一个已经出了的牌池BoardPond_ = new BoardPond()// 绘制地主和农民ClassTitle()PlayerGroupCreate()BoardLayer.draw()// 玩家出牌的下标let CurrentIndex = Loandlords_Paly_// 周期回合数;用以记录当前是否已走完一个周期(即三名玩家)let Bout = 0// 绑定出牌不出牌事件Button_.ShowButtonAndEvent('不出','出牌', async ()=>{// 如果当前的回合是玩家,且第一张出牌的也是玩家,// 如果玩家还没出牌,那么需要随机出一张牌if(!CurrentBorad && CurrentIndex==0){console.log('玩家随机出牌')// 将牌复原的合适的位置await RestoreBoard()}// 当前已过完一整个周期;那么牌堆处设置为空if(Bout==1){Bout = 0CurrentBorad = null}elseBout++// 将倒计时关闭TimeSecond.Close()// 下一个回合NextGame()}, async ()=>{let Current_index = NextIndex(CurrentIndex, -1)let myBoardif(Current_index == 0){myBoard = new BoardPlayer(TempBorad, 0)TempBorad.clear()}else // 机器人{myBoard = new BoardPlayer(RobotBoards, Current_index)RobotBoards = null}// 判定是否可以出牌if(JudgeBoard(myBoard)){// 将倒计时关闭TimeSecond.Close()Button_.ShowAndHide(false)// 将牌顶换为当前牌CurrentBorad = myBoard// console.log(myBoard)BoardPond_.SetAddBoard(myBoard)await PlayerMessage(PlayerList[Current_index], myBoard)}else {console.log('不能出牌')return}// 新回合的开始重新设置Bout = 0NextGame()})// 游戏回合function NextGame(){if(PlayEnd()) returnlet CurrentIndex_ = CurrentIndexTimeSecond.Show()DrawTabColor(CurrentIndex)let isRobat = falseif(CurrentIndex==0){IsLockClick = trueButton_.ShowAndHide(true)}else{IsLockClick = falseButton_.ShowAndHide(false)isRobat = true}PlayerList[CurrentIndex].Group.removeChildren()CurrentIndex = NextIndex(CurrentIndex)if(isRobat){setTimeout(() => {let _value = 0if(CurrentBorad!=null)_value = CurrentBorad.CurrentBoardCombin.Value// 机器人出牌RobotBoards = new BoardType(PlayerList[CurrentIndex_].BoardList).GetAllType('Y1', _value)if(RobotBoards==null)ButtonOne1.click()elseButtonOne2.click()}, 1000);}}NextGame()
}// 游戏结束
function PlayEnd(){let isEnd = falseif(PlayerList[0].BoardList.length==0){alert('游戏结束;你赢了')isEnd = true}if(PlayerList[1].BoardList.length==0){alert('游戏结束;你上家赢了')isEnd = true}if(PlayerList[2].BoardList.length==0){alert('游戏结束;你下家赢了')isEnd = true}if(isEnd){IsLockClick = falseTimeSecond.Close()Button_.HideButtonAndDeEvent()}return isEnd
}// 下一个玩家
function NextIndex(CurrentIndex_, Line=1){CurrentIndex_-=Lineif(CurrentIndex_<0)CurrentIndex_=2else if(CurrentIndex_>2)CurrentIndex_=0return CurrentIndex_
}// 判定系统,判定是否可以出牌
// 当牌顶为空,且牌顶数量与牌顶的真实价值小于当前出牌真实价值
// 且类型一致
function JudgeBoard(CurrentBoard_){if(CurrentBoard_.CurrentBoardCombin==null) return falseif(CurrentBorad==null) return trueif(CurrentBorad.CurrentBoardCombin.Value < CurrentBoard_.CurrentBoardCombin.Value)if(CurrentBorad.CurrentBoardCombin.Type == CurrentBoard_.CurrentBoardCombin.Type)if(CurrentBorad.CurrentBoardCombin.Length < CurrentBoard_.CurrentBoardCombin.Length || CurrentBoard_.CurrentBoardCombin.Type != 'Y8')return truereturn false
}// 玩家们的手牌绘制区
function PlayerGroupCreate(){PlayerList[0].Group = new Konva.Group()PlayerList[1].Group = new Konva.Group()PlayerList[2].Group = new Konva.Group()BoardLayer.add(PlayerList[0].Group)BoardLayer.add(PlayerList[1].Group)BoardLayer.add(PlayerList[2].Group)
}// 将牌按照大小进行排序
function BoardSort(BoardList){// 先将牌面进行排序let len = BoardList.lengthfor (var i = 0; i < len - 1; i++) {for (var j = 0; j < len - 1 - i; j++) {let value_1 = BoardMarkMap.get(BoardList[j].surface)let value_2 = BoardMarkMap.get(BoardList[j+1].surface)if (value_1 < value_2) { // 相邻元素两两对比var temp = BoardList[j+1]; // 元素交换BoardList[j+1] = BoardList[j];BoardList[j] = temp;}}}
}// 开始加载游戏
window.onload = function(){ButtonDiv = document.getElementById('ButtonDiv')ButtonOne1 = document.getElementById('CallLandLord')ButtonOne2 = document.getElementById('RobLandLord')TxtInfo = document.getElementById('txtInfo')// 首先必须添加一个场景Stage = new Konva.Stage({container: '#bodyPlayer', // id of container <div>width: width,height: height});BoardLayer = new Konva.Layer()Stage.add(BoardLayer)DrawTable()InitBoard()SendBorad()}// 执行动画,第一个参数,动画执行函数
function RAF(){}
veriable.js
/*** 作者:0.活在风浪里 v1.5.0* 变量初始化,记录有关游戏的所有全局变量*///---------------------------------------游戏基本变量// 牌面映射,字面量=》价值量
var BoardMarkMap = new Map();
BoardMarkMap.set('3', 3)
BoardMarkMap.set('4', 4)
BoardMarkMap.set('5', 5)
BoardMarkMap.set('6', 6)
BoardMarkMap.set('7', 7)
BoardMarkMap.set('8', 8)
BoardMarkMap.set('9', 9)
BoardMarkMap.set('0', 10) // 10
BoardMarkMap.set('J', 11)
BoardMarkMap.set('Q', 12)
BoardMarkMap.set('K', 13)
BoardMarkMap.set('A', 14)
BoardMarkMap.set('2', 15)
BoardMarkMap.set('C', 50) // 小鬼
BoardMarkMap.set('G', 100) // 大鬼// 牌组合类型映射
var BoardTypeMap = new Map()
BoardTypeMap.set('Y1', '单牌')
BoardTypeMap.set('Y2', '对子')
BoardTypeMap.set('Y3', '三带')
BoardTypeMap.set('Y4', '顺子')
BoardTypeMap.set('Y5', '连对')
BoardTypeMap.set('Y6', '飞机')
BoardTypeMap.set('Y7', '四带二')
BoardTypeMap.set('Y8', '炸弹')
// BoardTypeMap.set('Y9', '王炸')// 牌的绘制容器管理,可以通过该容器索引到任何一张牌的绘制内容
var BoardDrawMap = new Map()// keys集合;获取牌的字面量如K,J,Q....
var BoardMarkMapKes = Array.from(BoardMarkMap.keys())//---------------------------------------游戏结构性逻辑变量(包括绘制的逻辑变量)// 画布的大小
const width = 844
const height = 390// 牌有关的全局变量
const BoardWidth = 45
const BoardHight = 80// 按钮的大小
const ButtonWidth = 120
const ButtonPadding = 20// 每个回合等待的时间
const EveryTime = 20
// 按钮的容器:控制整体是否显示与否
var ButtonDiv = document.getElementById('ButtonDiv')
// 初始:叫地主=》过牌
var ButtonOne1 = document.getElementById('CallLandLord')
// 初始:不叫=》出牌
var ButtonOne2 = document.getElementById('RobLandLord')
// 头顶信息框
var TxtInfo = document.getElementById('txtInfo')// 玩家手牌图层
var MyBoardGroup = null
// 机器人手牌图层
var NotMeBoard_Group = null//---------------------------------------游戏内容逻辑变量
// 牌面绘制映射
var GrawBoard = new Map()
// 牌面选中集合
var TempBorad = new Map()
// 是否锁住点击事件
var IsLockClick = false
// 有几个玩家发完牌
var SendBoard = 0
// 机器人牌面
var RobotBoards// 玩家
var PlayerList = new Array()
// 自己,
PlayerList[0] = new Player([],'玩家一', 'SaddleBrown')
// 左边上家
PlayerList[1] = new Player([],'玩家二', 'DarkOliveGreen')
// 右边下家
PlayerList[2] = new Player([],'玩家三', 'MediumSlateBlue')// 场景
var Stage = null
// 是否已出牌或者叫了地主
var isSelect = false
// 地主索引=》对应的是玩家的下标
var Loandlords_Paly_ = -1// 图层
var BoardLayer = null
// 当前桌面牌堆;如果为null,则表明当前可以随意出牌
// 不然下一家必须接牌;类型:BoardPond
var CurrentBorad = null
// 牌池
var BoardPond_ = null
// 中间的桌子
var Tab_ = null// 生成各种牌
var BoardListGameInit = new Array();
for(var i=0;i<15;i++){var key = BoardMarkMapKes[i]if(key=='C'){BoardListGameInit.push(new Board(key, '🧛', i*4))continue}else if(key=='G'){BoardListGameInit.push(new Board(key, '🧙♂️', 53))continue}BoardListGameInit.push(new Board(key, '♠', i*4))BoardListGameInit.push(new Board(key, '♥', i*4+1))BoardListGameInit.push(new Board(key, '♣', i*4+2))BoardListGameInit.push(new Board(key, '♦', i*4+3))
}
index.html
<!DOCTYPE html>
<head><meta http-equiv="Content-Type" content="text/html" charset="UTF-8"><meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" ><title></title><script src="script/konva.min.js"></script><script src="script/GrawGame.js" ></script><script src="script/GameEntity.js" ></script><script src="script/veriable.js" ></script><script src="script/PlayGame.js" ></script><style>body {position: fixed;width: 100%;height: 390px;padding: 0;margin: 0;overflow: hidden;}.ButtonDiv{width: 90px;height: 20px;text-align: center;border-radius: 5px;padding: 5px;cursor: pointer;position:absolute;}#CallLandLord{top: 230px;left: calc(50% - 120px);background-color: aqua;}#RobLandLord{top: 230px;left: calc(50%);background-color: rgb(244, 158, 11);}#ButtonDiv{display: none;}@media screen and (orientation: portrait) {body {position: absolute;width: 100vh;height: 390px;top: 0;left: 100vw;-webkit-transform: rotate(90deg);-moz-transform: rotate(90deg);-ms-transform: rotate(90deg);transform: rotate(90deg);transform-origin: 0% 0%;}}@media screen and (orientation: landscape) {body {position: absolute;top: 0;left: 0;width: 100vw;height: 390px;}}
</style>
</head>
<body ><div id="bodyPlayer" style="margin: 0 auto; max-width: 844px; width: 100%; height: 390px; border: solid 1px;"></div><div style="width: 300px; text-align: center;position: absolute; top: 3%; left: calc(50% - 150px);" id="txtInfo"> 信息 </div><div id="ButtonDiv"><div id="CallLandLord" class="ButtonDiv" > 不叫 </div><div id="RobLandLord" class="ButtonDiv" > 叫地主 </div></div><script></script>
</body></html>
以上则是全部代码,复制项目直接运行index.html,
结语:
本文到这也要搁笔了,感谢你们阅读至此!感谢阅读,多多提供意见参考,评论区随意放肆评论,我会一 一读,真诚参考,
相关文章:

开源项目 —— 原生JS实现斗地主游戏 ——代码极少、功能都有、直接粘贴即用
目录 效果如下 目录结构 GameEntity.js GrawGame.js konva.min.js PlayGame.js veriable.js index.html 结语: 前期回顾 卡通形象人物2 写代码-睡觉 丝滑如德芙_0.活在风浪里的博客-CSDN博客本文实现了包含形象的卡通小人吃、睡、电脑工作的网页动画https://…...
Linux第四讲
目录 四、shell脚本 4.1 shell和shell脚本 4.2 脚本语言分类 4.2.1 编译型语言 4.2.2 解释型语言 4.2.3 脚本语言 4.3 shell常见种类 4.3.1 shell分类介绍 4.3.2 查看bash版本 4.3.3 sh和bash的关系 4.4 脚本书写规范 4.4.1 选择解释器 4.4.2 开发规范 4.5 shell…...
Redis 持久化
持久化是指数据写到物理硬盘里,即便程序崩溃、或者电脑重启,依然能够恢复。Redis提供了两种持久化机制:RDB和AOF。 RDB(Redis Database): RDB文件相当于内存快照,保存了某个时间点数据库信息。使用RDB文件恢复很简单,将…...

Python语言零基础入门教程(十三)
Python 字典(Dictionary) 字典是另一种可变容器模型,且可存储任意类型对象。 字典的每个键值 key:value 对用冒号 : 分割,每个键值对之间用逗号 , 分割,整个字典包括在花括号 {} 中 ,格式如下所示: d {key1 : value1, key2 : …...

江苏五年制专转本应该复习几轮?
五年制专转本应该复习几轮? 据调查统计:2022年专转本17%的考生复习三轮及以上,23%的考生复习了两轮。这两类的考生录取率高至85%。可见复习轮数多,专转本上岸的概率也大。综合多方因素,建议同学们专转本复习四轮&#…...

微信小程序的优化方案之主包与分包的研究
什么是分包? 某些情况下,开发者需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载。 在构建小程序分包项目时,构建会输出一个或多个分包。每个使用分包小程序必定含有一个主包。所谓的…...
从手工测试转型web自动化测试继而转型成专门做自动化测试的学习路线。
在开始之前先自学两个工具 商业web自动化测试工具请自学QTP;QTP的学习可以跳过,我是跳过了的。 开源web自动化测试工具请自学Selenium;我当年是先学watir(耗时1周),再学selenium(也耗时1周&…...
【计组笔记03】计算机组成原理之系统五大部件介绍、主存模型和CPU结构介绍
这篇文章,主要介绍计算机组成原理之系统五大部件、主存模型和CPU结构。 目录 一、计算机五大部件 1.1、体系结构 (1)冯诺依曼体系结构...
微信小程序解析用户加密数据
微信公众号 IT果果日记前言在上一篇文章“微信小程序如何获取用户信息”中我们完成了用户明文数据的校验工作,本文将学习解密用户的非明文用户信息,也就是获取用户的openId和unionId。解密调用wx.getUserProfile后将返回encryptedData和iv两个数据。encr…...

毕业四年换了3份软件测试工作,我为何仍焦虑?
今天一看日历:2023.2.11 ,才突然意识到自己毕业已经四年了。四年时间里一直在测试行业摸爬滚打,现在是时候记录一下了。 下面我来分享下我这4年软件测试经验及成长历程,或许能帮助你解决很多工作中的迷惑。 01、我是如何开始做…...
嵌入式C基础知识(7)
是否可以传递任何参数并从 ISR 返回值不可以。不能传递任何参数并从 ISR 返回值。 ISR 不返回任何内容,并且不允许传递任何参数。 当硬件或软件事件发生时调用 ISR,而代码不会调用它。 这就是为什么不向 ISR 传递参数的原因。 由于代码不调用 ISR&#x…...

大数据系列之:安装pulsar详细步骤
大数据系列之:安装pulsar详细步骤一、Pulsar版本和jdk对应关系二、安装JDK三、设置和激活jdk环境变量四、下载和解压Pulsar五、查看Pulsar目录六、启动Pulsar standalone cluster七、创建Kafka Topic八、往Topic写入数据九、消费pulsar的Topic一、Pulsar版本和jdk对…...

色彩-基础理论
颜色三大指标 色相 色相是颜色的一个属性,只有黑白灰没有色相这个属性(那银灰色是什么?) 颜色的相貌,指的也是给颜色一个名字 例如:暗红、酒红、土黄、墨绿 饱和度 颜色的鲜艳程度 纯度 饱和度主要取决于含色成分和消色成分&a…...

1629_MIT_6.828_xv6_chapter1操作系统的组织
全部学习汇总:GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 这一次整理一下操作系统组织相关的知识,主要还是xv6教学操作系统相关的知识。当然,很多知识在这类技术领域是通用的。 1. 操作系统的主要功能…...

基于Golang哈希算法监控配置文件变化
SHA(secure hashing algorithm)表示安全哈希算法.SHA是MD5的修正版本,用于数据摘要和认证。哈希和加密类似,唯一区别是哈希是单项的,即哈希后的数据无法解密。SHA有不同的算法,主要包括SHA-1, SHA-2, SHA-256, SHA-512, SHA-224, …...

关于一笔画问题的一些思考(欧拉路Fleury算法、逐步插入回路法、以及另一种可能的解法)
前言这是一个经典的图论问题了最近复习离散的时候又恰好看到了,发现自己以前的解法似乎有点bug然后开始出反例卡自己,结果发现卡不掉?然后再好好想了想,发现这个看起来有问题的做法可能确实没问题。注意:欧拉路、欧拉回…...

vlookup怎么用详细步骤,看这一篇就够了
1、vlookup函数:使用方法 以下便是vlookup函数,功能、语法和参数用法: excel函数vlookup 2、vlookup函数:查询参数 首先,选中F2单元格,然后在编辑栏输入函数公式:VLOOKUP(E2,B&…...

雅思经验(9)之小作文常用词汇总结
写作:关于趋势的上升和下降在小作文中,真的是非常常见的,所以还是要积累一下。下面给出了很多词,但是在雅思写作中并不是词越丰富,分数就越高的。雅思写作强调的是准确性:在合适的地方用合适的词和句法。不…...
【Python语言基础】——Python NumPy 数组创建
Python语言基础——Python NumPy 数组创建 文章目录 Python语言基础——Python NumPy 数组创建一、Python NumPy 数组创建一、Python NumPy 数组创建 创建 NumPy ndarray 对象 NumPy 用于处理数组。 NumPy 中的数组对象称为 ndarray。 我们可以使用 array() 函数创建一个 NumP…...
【大数据】Hadoop-Kms 安装及相关详细配置,看完你就会了
简介 Hadoop KMS是基于Hadoop的KeyProvider API的加密密钥管理服务器,它提供了使用REST API通过HTTP进行通信的客户端和服务器组件。 客户端是一个KeyProvider实现,使用KMS HTTP REST API与KMS交互。 KMS及其客户端具有内置的安全性,它们支…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...