Egret社区

源码分享_麻将逻辑代码

2017-4-14 00:35
649835
本帖最后由 nodep 于 2017-4-18 21:42 编辑

简单棋牌编写思路,如果没思路最好别拿别人代码,得不偿失源码在楼下

mahjong_demo.zip

773.92 KB, 阅读权限: 10, 下载次数: 556, 下载积分: 银子 -1

售价: 1 银子  [记录]

分享到 :
8 人收藏
各类游戏开发,软件开发,合作项目,项目融资
as3,egret,c++,golang,erlang,php,nodejs,各类大型建站,大数据,人工智能项目.
只要你有好的idea,随时联系我们

35 个回复

倒序浏览
nodep  略有小成 | 2017-4-14 16:24:44
如图所示,实现了主逻辑流程
在LogicDemo里可配置自己的初始牌
另外碰可以照着杠抄过来,因为我自己并不做H5工作,还有自己的东西要写。棋牌的demo就到这里了。斗地主什么的都和这个差不太多。
胡牌的逻辑:
首先在加上(当前打出来的或自己摸起来的情况下)。不能有独立存在的牌,这个是思路起点。接下来可以用递归组合的方式组合起来。只要是
没有独立的牌,并且对子不超过1个(当然有七对的逻辑),所以每个地方的胡牌不一样,这个还是要自己写了。过周末了,太懒,不想写了。

参考图

参考图
nodep  略有小成 | 2017-4-14 17:19:28

主要目录结构,比较粗暴

主要目录结构,比较粗暴

[C++] 纯文本查看 复制代码
/**逻辑 这个是后端完成的,这里模拟后端和延迟通信*/
class LogicDemo {

	private _cards: Array<CardData>;
	public __dealRing:number = 0;
	private _getted:boolean = true;
	private static _ins: LogicDemo;

	public aiCard:CardData;
	//---在这里配置你想摸到的牌,方便测试---
	private _initSelfCardTypes:number[] = [1,1,1,1,2,2,2,2];
	private _initSelfCardNum:number[] = [1,1,1,1,2,2,2,2];

	public constructor() {
	}

	public static get ins(): LogicDemo {
		if (!LogicDemo._ins)
			LogicDemo._ins = new LogicDemo();
		return LogicDemo._ins;
	}

	/**发牌 */
	public dealCards(): void {
		SelfCardGroup.getIns().setInfo("正在发牌...");
		this.__dealRing = 0;
		this.createCards();//创建
		this.shuffle();//洗牌
		this.deal();
	}

	//我的回合结束
	public optionEnd():void
	{
		this._getted = false;
		this.__dealRing = 0;
		this.continueGame();
	}

	//继续
	public goOn():void
	{
		this.continueGame();
	}

	/**发一张牌给我,从开头 */
	public shiftOne():void
	{
		SelfCardGroup.getIns().addCard(this._cards.shift(),false);
		SelfCardGroup.getIns().gogo();
	}

	/**开始游戏 */
	private continueGame():void
	{
		if(this._getted)//等待玩家出牌
		{
			SelfCardGroup.getIns().gogo();
			return;
		}
		else
		{
			this.__dealRing++;
			if(this.__dealRing%4!=0)//给计算机发牌
			{
				this.jump();
			}
			else//给自己发牌
			{
				DelayCall.call(1000,this.dealSelf,this);
			}
		}
	}

	/**计算机出牌,僵尸摸什么打什么 */
	private jump():void
	{
		this.aiCard = this._cards.pop();
		SelfCardGroup.getIns().looklook();
	}

	/**给自己发一张牌 */
	private dealSelf():void
	{
		//玩家得到一张牌后等待玩家操作
		this._getted = true;
		SelfCardGroup.getIns().addCard(this._cards.pop(),false);
	}

	/**开始发牌逻辑 */
	private deal():void
	{
		if(this.__dealRing<3)//给所有人发牌,一次发4张
		{
			//给自己发一张,其他人直接丢掉(测试用)
			SelfCardGroup.getIns().addCard(this._cards.pop(),true);
			SelfCardGroup.getIns().addCard(this._cards.pop(),true);
			SelfCardGroup.getIns().addCard(this._cards.pop(),true);
			SelfCardGroup.getIns().addCard(this._cards.pop(),true);
			this._cards.splice(this._cards.length-12,12);
			DelayCall.call(500,this.deal,this);
		}
		else//给自己补发一张
		{
			this._getted = true;
			this.__dealRing = 0;
			SelfCardGroup.getIns().addCard(this._cards.pop(),true);
			this._cards.pop();
			this._cards.pop();
			this._cards.pop();
			SelfCardGroup.getIns().addCard(this._cards.pop(),true);
			SelfCardGroup.getIns().sortCards(1000);
			this.continueGame();
			return;
		}
		this.__dealRing++;
	}

	//创建108
	private createCards(): void {
		this._cards = new Array<CardData>();
		//108张,如果要玩东南风需要扩展.对应筒条万
		for (var i: number = 1; i <= 3; i++) {
			for (var c: number = 1; c <= 9; c++) {
				this._cards.push(new CardData(i, c));
				this._cards.push(new CardData(i, c));
				this._cards.push(new CardData(i, c));
				this._cards.push(new CardData(i, c));
			}
		}
	}

	private _hzList:number[] = [];

	/**洗牌 */
	private shuffle():void{
		this._hzList = [];
		var cards:Array<CardData> = new Array<CardData>();
		var key:any;
		var card:CardData;
		for(key in this._cards)
		{
			card = this._cards[key];
			cards.splice(Math.floor(cards.length*Math.random()),0,card);
		}
		//----------在这里洗盒子-----------洗前12个就好了
		var insertIndex:number = 0;
		for(var i:number=0;i<this._initSelfCardTypes.length;i++)
		{
			var insertCard:CardData = this.getChildBy(cards,this._initSelfCardTypes[i],this._initSelfCardNum[i]);
			this._hzList.push(insertCard.id);
			if(insertCard==null)
				console.log("异常");
			cards.splice(cards.indexOf(insertCard),1);
			cards.splice(cards.length-insertIndex,0,insertCard)
			insertIndex++;
			if(insertIndex%4==0)
				insertIndex+=12;
		}
		this._cards = cards;
	}

	/**取 */
	private getChildBy(from:Array<CardData>,type:number,num:number):CardData
	{
		var key:any;
		var dd:CardData;
		for(key in from)
		{
			dd = from[key];
			if(dd.cType == type && dd.cNum == num && this._hzList.indexOf(dd.id)<0)
				return dd;
		}
		return null;
	}
}

[C++] 纯文本查看 复制代码
/**自己的牌组显示对象 */
class SelfCardGroup extends egret.DisplayObjectContainer {

	private _cards: Array<CardData> = new Array<CardData>();//手上的牌
	private _deskCards: Array<CardData> = new Array<CardData>();//碰和杠的牌

	private _cardBox: HBox;
	private _deskBox: HBox;
	private _focusCard: CardSprite;
	private _debugInfo: egret.TextField;
	private _focusType: string = "";
	private _cardKey: string = "";
	private _gangIndex: number = 0;
	private _gangArray: string[] = [];

	/**自己的牌面显示对象只会有一个 */
	private static _ins: SelfCardGroup;

	/**私有掉构造 */
	private constructor() {
		super();
		this._cardBox = new HBox();
		this._cardBox.touchEnabled = true;
		this.addChild(this._cardBox);
		this._deskBox = new HBox();
		this.addChild(this._deskBox);
		this.addChild(OptionBar.getIns());
		this._debugInfo = new egret.TextField();
		this._debugInfo.size = 30;
		this.addChild(this._debugInfo);
	}

	public static getIns(): SelfCardGroup {
		if (!SelfCardGroup._ins)
			SelfCardGroup._ins = new SelfCardGroup();
		return SelfCardGroup._ins;
	}

	public setInfo(str: string): void {
		this._debugInfo.text = str;
		this._debugInfo.x = (this.stage.stageWidth - this._debugInfo.textWidth) / 2;
		this._debugInfo.y = 10;
	}

	/**得到一张牌 */
	public addCard(cd: CardData, isDeal: boolean = false): void {
		var card: CardSprite;
		if (isDeal)//如果是发牌
		{
			card = new CardSprite(-1, cd);
			card.delayFace();
		}
		else//如果是摸牌
		{
			card = new CardSprite(1, cd);
			this.gogo();
		}
		this._cards.push(cd);
		this._cardBox.addEle(card);
		this.resize();
	}

	public resize(): void {
		this._cardBox.y = this.stage.stageHeight - this._cardBox.height;
		this._cardBox.x = (this.stage.stageWidth - this._cardBox.width) / 2;
		this._deskBox.x = 10;
		this._deskBox.y = this.stage.stageHeight - 400;
		OptionBar.getIns().x = this.stage.stageWidth - OptionBar.getIns().width;
		OptionBar.getIns().y = this.stage.stageHeight - 400;
		this._debugInfo.x = (this.stage.stageWidth - this._debugInfo.textWidth) / 2;
		this._debugInfo.y = 10;
	}

	/**make your choose */
	public gogo(): void {
		this._focusType = "";//重置当前操作类型
		SelfCardGroup.getIns().setInfo("该你出牌...");
		this._cardBox.addEventListener(egret.TouchEvent.TOUCH_TAP, this.tabHandler, this)
		this.checkOpt("put");
	}

	/**do what? */
	public looklook(): void {
		this.setInfo("【" + LogicDemo.ins.__dealRing + "】出牌=" + CardSprite.getTxt(LogicDemo.ins.aiCard) + " 你要怎么操作?");
		OptionBar.getIns().changeBtnState("过", true);
		//如果有其他可操作的,放开确认
		this.checkOpt("turn");
	}

	/**点击事件 */
	private tabHandler(evt: egret.TouchEvent): void {
		this._focusType = "";//重置操作类型
		if (typeof (evt.target) != "object")
			return;
		var cCount: number = this._cardBox.numChildren;
		for (var i: number = 0; i < cCount; i++)
			this._cardBox.getChildAt(i).y = 0;
		var target: CardSprite = evt.target as CardSprite;
		if (this._focusCard == target) {
			this._focusCard = null;
			this.checkOpt("put");
			return;
		}
		else {
			this._focusCard = target;
			this._focusCard.y = -20;
			this.checkOpt("put");
		}
	}

	/**检查当前可操作项目,自己的出牌环节 */
	public checkOpt(t: string): void {
		if (t == "put")//检查出牌
		{
			//检查自摸
			//检查暗杠,检查明杠
			OptionBar.getIns().changeBtnState("杠", this.checkGang());
			//检查出牌和确认
			OptionBar.getIns().changeBtnState("确认", true);
			//这里不能过牌
			OptionBar.getIns().changeBtnState("过", false);
		}
		else if (t == "turn")//检查所有
		{
			//检查胡牌
			//检查明杠
			var gangFlag: boolean = this.checkGang(LogicDemo.ins.aiCard);
			OptionBar.getIns().changeBtnState("杠", gangFlag);
			//检查碰牌
			//如果有其他可操作项,这里就有确认
			OptionBar.getIns().changeBtnState("确认", gangFlag);
			//这里可以过
			OptionBar.getIns().changeBtnState("过", true);
		}
	}

	/**排序 */
	public sortCards(delayTime: number = 0): void {
		if (delayTime > 0) {
			DelayCall.call(delayTime, this.sortCards, this);
			return;
		}
		//------将牌进行排序-------
		this._cards.sort(function (a: CardData, b: CardData): number {
			if (a.cType * 10 + a.cNum > b.cType * 10 + b.cNum)
				return 1;
			return -1;
		});
		for (var i: number = 0; i < this._cards.length; i++) {
			(this._cardBox.getChildAt(i) as CardSprite).setData(this._cards[i]);
		}
	}

	/**操作来了 */
	public optionHandler(type: string): void {
		switch (type) {
			case "确认": this.option_queren();
				break;
			case "过":
				this.clearOption();
				LogicDemo.ins.goOn();
				break;
			case "杠":
				this._focusType = "杠";
				this.changeGang();
				break;
			default:
				console.log("还未处理这个类型" + type);
				break;
		}
	}

	private clearOption(): void {
		OptionBar.getIns().changeBtnState("过", false);
		OptionBar.getIns().changeBtnState("确认", false);
		OptionBar.getIns().changeBtnState("杠", false);
		OptionBar.getIns().changeBtnState("胡", false);
		OptionBar.getIns().changeBtnState("碰", false);
	}

	private doEnd(): void {
		this._cardBox.removeEventListener(egret.TouchEvent.TOUCH_TAP, this.tabHandler, this)
		this.clearOption();
		LogicDemo.ins.optionEnd();
	}

	/**确认的操作响应 */
	private option_queren(): void {
		if (this._focusType == "")//出牌
		{
			if (this._focusCard == null)
				this.setInfo("点击一张你要出的牌");
			else {
				var delIndex: number = this._cards.indexOf(this._focusCard.data);
				this._cardBox.removeEle(this._focusCard);
				this._cards.splice(delIndex, 1);
				this.doEnd();
				this.sortCards(0);
			}
		}
		else {
			switch (this._focusType) {
				case "杠":
					this.removeCardToDesk(this._cardKey, 4);
					this.clearOption();
					LogicDemo.ins.shiftOne();
					break;
			}
		}
	}

	//-------------------------逻辑部分粗暴解决------------------------
	//为了更好的性能和维护性,应该在玩家牌组发生变化的时候就计算好哪些是可以杠,哪些是可以碰的。这里懒的改了,随手写

	/**显示多少个牌 */
	private showKeys(key: string, count: number): void {
		var cCount: number = this._cardBox.numChildren;
		var i: number = 0;
		for (i = 0; i < cCount; i++)
			this._cardBox.getChildAt(i).y = 0;
		var cs: CardSprite;
		var cc: number = 0;
		for (i = 0; i < cCount; i++) {
			cs = this._cardBox.getChildAt(i) as CardSprite;
			if (cs.data.key != key)
				continue;
			cs.y = -20;
			cc++;
			if (cc >= count)
				return;
		}

	}

	/**移动牌到桌子 */
	private removeCardToDesk(key: string, count: number): void {
		var cCount: number = this._cardBox.numChildren;
		var i: number = 0;
		for (i = 0; i < cCount; i++)
			this._cardBox.getChildAt(i).y = 0;
		var cc: number = 0;
		if (LogicDemo.ins.aiCard && LogicDemo.ins.aiCard.key == key) {
			cc++;
			this._deskCards.push(LogicDemo.ins.aiCard);
			LogicDemo.ins.aiCard = null;
		}
		var keyc: any;
		var cd: CardData;
		for (keyc in this._cards) {
			if (cc >= count)
				break;
			cd = this._cards[keyc];
			if (cd.key == key) {
				cc++;
				this._deskCards.push(cd);
			}
		}
		for (keyc in this._deskCards) {
			cd = this._deskCards[keyc];
			i = this._cards.indexOf(cd);
			if (i >= 0)
			{
				this._cards.splice(i, 1);
				this.removeCardByID(cd.id);
			}
		}
		this.sortCards(0);
		var index:number = 0;
		if(count==4)
			count=1;
		while(this._deskBox.numChildren<this._deskCards.length)
		{
			var putIndex:number = this._deskBox.numChildren;
			var dsc:CardSprite = new CardSprite(index++<count?1:-1,this._deskCards[putIndex]);
			this._deskBox.addEle(dsc);
		}
	}

	/**移除显示 */
	private removeCardByID(cid: number): void {
		var cCount: number = this._cardBox.numChildren;
		var i: number = 0;
		for (i = 0; i < cCount; i++) {
			var cs:CardSprite = this._cardBox.getChildAt(i) as CardSprite;
			if(cs.data.id == cid)
			{
				this._cardBox.removeEle(cs,false);
				break;
			}
		}
	}


	private addCardToMap(map: Map<string, number>, c: CardData, checkHas: boolean = false): void {
		if (map.get(c.key) == null && !checkHas)
			map.set(c.key, 1);
		else
			map.set(c.key, map.get(c.key) + 1);
	}

	/**
	 * 明暗杠检查
	 * 重点:无论是明杠还是按杠,首先必须受伤要有这个牌.
	 */
	private checkGang(fromCard: CardData = null): boolean {
		var oc: CardData = fromCard;
		this._gangArray = [];
		this._gangIndex = 0;
		var handMap: Map<string, number> = new Map<string, number>();
		var keys: string[] = [];
		if (fromCard != null)//说明是杠他人的牌,只算手牌
		{
			this.addCardToMap(handMap, fromCard);
			keys.push(fromCard.key);
		}
		var key: any;
		for (key in this._cards) {
			fromCard = this._cards[key];
			this.addCardToMap(handMap, fromCard, oc != null);
			if (keys.indexOf(fromCard.key) < 0)
				keys.push(fromCard.key);
		}
		for (key in this._deskCards) {
			fromCard = this._deskCards[key];
			this.addCardToMap(handMap, fromCard, true);
		}
		for (key in keys) {
			if (handMap.get(keys[key]) >= 4)
				this._gangArray.push(keys[key]);
		}
		return this._gangArray.length > 0;
	}

	/**更换杠的组合 */
	private changeGang(): void {
		this._cardKey = this._gangArray[this._gangIndex];
		this.showKeys(this._cardKey, 4);
		this._gangIndex++;
		if (this._gangIndex >= this._gangArray.length)
			this._gangIndex = 0;
	}
}

大概结构和最主要的两个类,都是比较粗暴的解决了。
从main.entergame开始,这里没有改框架生成的代码。

逻辑代码的衔接主要在
SelfCardGroup的checkOpt();
"put" = 出牌
"turn" = 等待过牌

optionBar中用了中文,主要是为了方便阅读。
CardType里1-3对应筒条万
yjtx  官方团队 | 2017-4-14 09:36:27
monkeykeyly  初学乍练 | 2017-4-14 11:06:13
小白坐等收菜
yung  初窥堂奥 | 2017-4-14 11:13:27
白蚂蚁  自成一派 | 2017-4-14 11:16:10
深而不多  登堂入室 | 2017-4-14 12:34:31
哪里拿啊,想看看怎么做的
Asan阿弎  登堂入室 | 2017-4-14 13:14:28
坐等看看
nodep  略有小成 | 2017-4-14 13:50:47
进度汇报

上午11点开始,看看进度。正在补几个逻辑函数

上午11点开始,看看进度。正在补几个逻辑函数
深而不多  登堂入室 | 2017-4-14 14:27:03

有没有直播课程啊,我想看看egret开发的游戏的生命周期
蓝枫  登堂入室 | 2017-4-14 14:28:55
哈哈  自己写了呀   找不到外援了。。

点评

病了?  发表于 2017-4-14 14:50
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|京网文[2014]0791-191号|京ICP证150115号|Egret社区 ( 京ICP备14025619号

Powered by Discuz! X3.2 © 2001-2016 Comsenz Inc.

返回顶部