都て0てて年了,还有人不会打麻将?

4,058 阅读4分钟

创意闹新春,我正在参加「春节创意投稿大赛」,详情请看:春节创意投稿大赛

游戏地址:yain-tls.netlify.app/
开发框架:vue3+vite 运行平台:浏览器、适配移动端(横屏体验更佳)
gitee地址:gitee.com/yan_yin/mah…
欢迎大家体验

前言

又是新的一年,先给各位hxd和dl萌拜个早年,祝大家虎虎生威,如虎添翼,虎年大吉。

事物都有两面性,有的人认为打麻将不是一件好事,是赌博,但我觉得麻将只是个休闲游戏,是个社交活动,过年和朋友打一下午麻将,然后谁赢了请吃饭岂不快哉。做这个小教程的灵感来自于我朋友,因为他不会打麻将,但是过年很多朋友都要玩,他不玩就很不合群,于是在公司肝了两天便有了它,胡了有彩蛋哦!yain-tls.netlify.app/ 快去转给不会麻将的小伙伴,让他学会,过年便多了一份快乐。做的简单,不喜勿喷。

游戏规则

先上公式 :n*ABC + m*DDD + 1*EE
ABC:顺子 、DDD:三个相同 、EE:对子 | n>=0 、m>=0
由于对川麻较熟悉,所以只有大众和四川两种模式
大众模式就是满足上面的公式即可,川麻则是必须在缺一个花色的前提下满足公式

image.png

菜单及全局配置

目前有模式切换,是否自动摸牌和是否开启BGM,默认是大众、否、否。改变后用localStorage保存了状态。

模式切换

<div class="container">
      <da-zhong v-if="!model" />
      <si-chuan v-else />
</div>

没必要用路由,直接就通过一个变量切换两个组件

自动摸牌

在打出一张麻将之后再去调摸牌的函数即可

const delMj =  (index)=> {
     if([1,4,7,10,13].includes(data.handMj.length)){
       window.alert('请先摸牌')
     }else{
     data.handMj.splice(index,1)
     //摸牌的时候不排序  打出去的时候在排
     handMjSort()
     }
   }
  const handMjSort = ()=> {
     setTimeout(()=> {
      data.handMj.sort((a,b )=>a-b)
      //自动摸牌
     if(Number(localStorage.getItem('model2'))==1)getNewMj()
     },1000)
   }
   const getNewMj = ()=> {
     //摸牌后排序
      data.handMj.push(...data.remainMj.splice(0,1))
      checkMj()
      //手牌麻将重复数最多的麻将
      let max = findNum(data.handMj)
      //是否有杠
      if(max[0]==4){
        //有杠
        data.isGang = true
        data.gangId = max[1]
      }else{
        data.isGang = false
      }

   }

声音

由于只有一个BGM,所以直接写在了appDom上,点击开关的时候直接调方法

<audio hidden="true" ref="audio" loop controls>
    <source src="./assets/woop.mp3" type="audio/mpeg" />
 </audio>
const audio = ref();
audio.value.volume = 0.1
audio.value.play()
audio.value.pause()

思路

麻将ID

最先想的是一个对象{type:x,num:y},一个是花色,一个是数字,但发现在排序和验证是否胡的时候很麻烦,于是想着就1-27个数字,每个数字有4个,在验证胡牌的时候9,10,11会通过验证,但是因为花色不一样,是不能成顺子,所以最简单的ID应该就是1-9,11-19,21-29。生成108张之后再用random乱序。

for (let i = 1; i <28;i++) {
  for (let j = 0; j <4;j++) {
       //将  筒 条 万不同花色的隔开
    if(i<=9){
      mjArr.push(i)
    }else if(i<=18){
      mjArr.push(i+1)
    }else if(i<=27){
      mjArr.push(i+2)
    }
  }
}
mjArr = mjArr.sort(()=>0.5 - Math.random())

流程

初始化先从108张中取13,排序摸一张牌

const data = reactive({
     mjArr,
     selectMj:[],   //获取随机的13个
     remainMj:[], //除去13个剩余的无序麻将
     handMj:[],//手牌,除去杠的牌
     footMj:[],//碰或者杠的牌,
     isGang:false, //是否可以杠
     gangId:'', //杠的id
     isCheck:false, //是否胡牌
     isNone:'none',//是否展示胡了图片
   })
onMounted(()=> {
     data.selectMj = mjArr.slice(0, 13) 
     data.remainMj = mjArr.slice(13)  
     data.handMj = [...data.selectMj] 
     data.handMj.sort((a,b )=>a-b)
     getNewMj()
   })

摸牌后先进行胡牌检测,再是否有杠的检测,按理来说如果有多个杠,是可以进行选择哪一个杠,但是由于时间原因没弄

const getNewMj = ()=> {
      data.handMj.push(...data.remainMj.splice(0,1))
      checkMj()
      //手牌麻将重复数最多的麻将
      let max = findNum(data.handMj)
      //是否有杠
      if(max[0]==4){
        //有杠
        data.isGang = true
        data.gangId = max[1]
      }else{
        data.isGang = false
      }
   }

打一张牌再排序,在牌数量不正确时不让打出并提示

//打出一张
   const delMj =  (index)=> {
     if([1,4,7,10,13].includes(data.handMj.length)){
       window.alert('请先摸牌')
     }else{
     data.handMj.splice(index,1)
     //摸牌的时候不排序  打出去的时候在排a
     handMjSort()
     }
   }
   //判断是否胡牌
   const checkMj = ()=> {
       let arr  = [...data.handMj].sort((a,b )=>a-b)
       data.isCheck = isHu(arr)
   }
   

川麻

两种模式其实差别不是很大,可以通过变量判断写到一个页面,但是嫌麻烦所以直接分开写了,川麻不同之处是首先在进入的时候有个蒙层然后选择定缺,之后在胡牌检测前先进行检测是否有花猪,之后的流程基本一样。

//判断是否胡牌(四川模式)
   const checkMj = ()=> {
     if(checkPig()){
       let arr  = [...data.handMj].sort((a,b )=>a-b)
       data.isCheck = isHu(arr)
     }
   }

image.png

1.先判断是否7对
2.去除一个对牌,将剩余的牌进行ABC/DDD检测,成功后将ABC/DDD删除再进行检测
3.还原牌后去除下一个不同的对牌在进行ABC/DDD

代码太多了就不放上面了。gitee.com/yan_yin/mah… ,参考了 https://gitee.com/w_xd/mahjong-utils ,里面还有各种情况,感兴趣的可以去看看

最后

本是四人游戏却只有一个人自娱自乐,但是初衷就是想让不会的xdm能学会。
做的随意,有什么问题敬请指出

既然都看到这儿了,不妨点个赞再走~