草庐IT

四川麻将判断胡牌,找到要听的牌

janbar 2023-03-28 原文

详细代码如下:

package main

import (
	"fmt"
	"strings"
)

func main() {
	s := "1w1w2d2d3d3d4w4w3w3w7d7d8d8d"
	mj := InitMahjong(s)
	mj.Print()
	fmt.Println(mj.Win())

	fmt.Println("-------------------------------")
	s = "1w2w2d3d4d5d3w4w5w6w7d8d8w9w"
	mj = InitMahjong(s)
	mj.Print()
	fmt.Println(mj.Win())

	fmt.Println("-------------------------------")
	s = "1w2w2d3d4d5d3w4w5w6w7d8d9w9w"
	mj = InitMahjong(s)
	mj.Print()
	fmt.Println(mj.Win())

	fmt.Println("-------------------------------")
	s = "2w2w2w4w4w4w5w5w6w6w7w3d3d"
	mj = InitMahjong(s)
	mj.Print()
	fmt.Println(strings.Join(mj.Solution(), "\n"))

	fmt.Println("-------------------------------")
	s = "2w2w2w4w4w4w5w6w6w6w7w8w9w"
	mj = InitMahjong(s)
	mj.Print()
	fmt.Println(strings.Join(mj.Solution(), "\n"))
}

type Mahjong struct {
	// 万,条,筒数量, 例如:
	//  cardNum[0][5]=3,表示6万有3张
	//  cardNum[1][8]=4,表示9条有4张
	//  cardNum[2][2]=1,表示3筒有1张
	cardNum [3][9]uint8
	// 每种花色数量, 例如:
	//  typeNum[0]=5,表示万有5张
	//  typeNum[1]=6,表示条有6张
	//  typeNum[2]=3,表示筒有3张
	typeNum [3]uint8
	// 4张一样的牌,杠数量
	gangNum uint8
	// w: 万,t: 条,d: 筒
	typedef string
}

func InitMahjong(data string) *Mahjong {
	mj := &Mahjong{typedef: "wtd"}
	for i := 1; i < len(data); i += 2 {
		for j := 0; j < 3; j++ {
			if data[i] == mj.typedef[j] {
				if num := data[i-1] - '1'; num < 9 {
					mj.typeNum[j]++
					mj.cardNum[j][num]++
				}
				break
			}
		}
	}
	return mj
}

func (mj *Mahjong) Print() {
	fmt.Print("当前牌: ")
	for i := 0; i < 3; i++ {
		for j := 0; j < 9; j++ {
			for k := mj.cardNum[i][j]; k > 0; k-- {
				fmt.Printf("%d%c,", j+1, mj.typedef[i])
			}
		}
	}
	fmt.Println()
}

func (mj *Mahjong) IsOneTypeWin(msg *strings.Builder, cards []uint8, num uint8, hasCouple *bool, mt byte) bool {
	// n * AAA + m * ABC + DD,如有4个一样的,则剔除后满足前面条件则胡牌
	if num == 0 {
		return true // 当前花色数量为0,计算完毕
	}

	for i := 0; i < 9; i++ {
		// 表示有对子,胡牌时只能有一个对子,因此判断 !*hasCouple
		if cards[i] >= 2 && !*hasCouple {
			cards[i] -= 2
			*hasCouple = true
			if mj.IsOneTypeWin(msg, cards, num-2, hasCouple, mt) {
				_, _ = fmt.Fprintf(msg, "[%d%c%d%c],", i+1, mt, i+1, mt)
				return true
			}
			cards[i] += 2
			*hasCouple = false
		}

		if cards[i] == 4 {
			cards[i] -= 4
			if mj.IsOneTypeWin(msg, cards, num-4, hasCouple, mt) {
				_, _ = fmt.Fprintf(msg, "[%d%c%d%c%d%c%d%c],",
					i+1, mt, i+1, mt, i+1, mt, i+1, mt)
				mj.gangNum++ // 有4个一样的,将杠数量+1
				return true
			}
			cards[i] += 4
		}

		if cards[i] >= 3 {
			cards[i] -= 3
			if mj.IsOneTypeWin(msg, cards, num-3, hasCouple, mt) {
				_, _ = fmt.Fprintf(msg, "[%d%c%d%c%d%c],",
					i+1, mt, i+1, mt, i+1, mt)
				return true // 组成3张一样的
			}
			cards[i] += 3
		}

		if i < 7 && cards[i] > 0 && cards[i+1] > 0 && cards[i+2] > 0 {
			cards[i]--
			cards[i+1]--
			cards[i+2]--
			if mj.IsOneTypeWin(msg, cards, num-3, hasCouple, mt) {
				_, _ = fmt.Fprintf(msg, "[%d%c%d%c%d%c],",
					i+1, mt, i+2, mt, i+3, mt)
				return true // 组成一个顺子
			}
			cards[i]++
			cards[i+1]++
			cards[i+2]++
		}
	}
	return false
}

/*
1. 和牌的基本牌型
  11,123,123,123,123
  11,123,123,123,111 (1111,下同)
  11,123,123,111,111
  11,123,111,111,111
  11,111,111,111,111

2. 和牌的特殊牌型
  11,11,11,11,11,11,11 (小七对)
  1,1,1,1,1,1,1,1,1,1,1,1,11 (十三幺)
  1,1,1,1,1,1,1,1,1,1,1,1,1,1 (七星不靠,全不靠)

注: 1=单张,11=将/对子,111=刻子,1111=杠,123=顺子
*/

func (mj *Mahjong) Win() (string, bool) {
	if mj.typeNum[0] > 0 && mj.typeNum[1] > 0 && mj.typeNum[2] > 0 {
		return "没有缺一门", false
	}

	var cnt [3][]string
	for i := 0; i < 3; i++ {
		for j := 0; j < 9; j++ {
			if n := mj.cardNum[i][j]; n > 4 {
				return fmt.Sprintf("%d%c有%d张",
					j+1, mj.typedef[i], n), false
			} else if n == 1 || n == 2 {
				// 将数量为1和2的牌记录下来,用于判断特殊牌型
				cnt[n] = append(cnt[n], fmt.Sprintf("%d%c",
					j+1, mj.typedef[i]))
			}
		}
	}

	// 2种花色总共14张时,判断特殊胡牌
	if mj.typeNum[0]+mj.typeNum[1]+mj.typeNum[2] == 14 {
		switch l1, l2 := len(cnt[1]), len(cnt[2]); {
		case l1 == 14:
			return "七星不靠: " + strings.Join(cnt[1], ","), true
		case l2 == 7:
			return "小七对: " + strings.Join(cnt[2], ","), true
		case l1 == 12 && l2 == 1:
			return "十三幺: " + strings.Join(cnt[1], ",") + ", 2 * " + cnt[2][0], true
		}
	}

	var (
		hasCouple bool
		msg       strings.Builder
		tmpCard   = make([]uint8, 9)
	)
	mj.gangNum = 0 // 重新统计杠牌数量
	for i := 0; i < 3; i++ {
		copy(tmpCard, mj.cardNum[i][:]) // 使用副本,不要修改mj.typeNum
		if !mj.IsOneTypeWin(&msg, tmpCard, mj.typeNum[i], &hasCouple, mj.typedef[i]) {
			return fmt.Sprintf("花色:%c 不满足胡牌", mj.typedef[i]), false
		}
	}

	if !hasCouple {
		return "没有对子,不能胡牌", false
	}

	// 所有手头的牌-14后多余的牌个数就是杠的数量,没有杠则刚好等于14
	if mj.typeNum[0]+mj.typeNum[1]+mj.typeNum[2]-14 != mj.gangNum {
		return "你丫诈和,牌的数量不对", false
	}
	return "正常胡牌: " + msg.String(), true
}

func (mj *Mahjong) Solution() (res []string) {
	for i := 0; i < 3; i++ {
		for j := 0; j < 9; j++ {
			if mj.cardNum[i][j] == 4 {
				continue // 已经有4张牌,跳过
			}

			mj.cardNum[i][j]++
			mj.typeNum[i]++ // 假设得到这张牌
			msg, ok := mj.Win()
			mj.cardNum[i][j]--
			mj.typeNum[i]--
			if ok { // 假设得到一张牌,能胡牌时该牌就是要听的牌
				res = append(res, fmt.Sprintf("%s听%d%c",
					msg, j+1, mj.typedef[i]))
			}
		}
	}
	return
}

结果如下:

当前牌: 1w,1w,3w,3w,4w,4w,2d,2d,3d,3d,7d,7d,8d,8d,       
小七对: 1w,3w,4w,2d,3d,7d,8d true                        
-------------------------------                          
当前牌: 1w,2w,3w,4w,5w,6w,8w,9w,2d,3d,4d,5d,7d,8d,       
七星不靠: 1w,2w,3w,4w,5w,6w,8w,9w,2d,3d,4d,5d,7d,8d true 
-------------------------------                          
当前牌: 1w,2w,3w,4w,5w,6w,9w,9w,2d,3d,4d,5d,7d,8d,       
十三幺: 1w,2w,3w,4w,5w,6w,2d,3d,4d,5d,7d,8d, 2 * 9w true 
-------------------------------                          
当前牌: 2w,2w,2w,4w,4w,4w,5w,5w,6w,6w,7w,3d,3d,          
正常胡牌: [5w6w7w],[4w5w6w],[4w4w4w],[2w2w2w],[3d3d],听4w
正常胡牌: [5w6w7w],[5w6w7w],[4w4w4w],[2w2w2w],[3d3d],听7w
正常胡牌: [5w6w7w],[4w5w6w],[4w4w],[2w2w2w],[3d3d3d],听3d
-------------------------------
当前牌: 2w,2w,2w,4w,4w,4w,5w,6w,6w,6w,7w,8w,9w,
正常胡牌: [7w8w9w],[6w6w6w],[4w4w],[3w4w5w],[2w2w2w],听3w
正常胡牌: [7w8w9w],[6w6w],[4w5w6w],[4w4w4w],[2w2w2w],听4w
正常胡牌: [7w8w9w],[6w6w6w],[5w5w],[4w4w4w],[2w2w2w],听5w
正常胡牌: [7w8w9w],[6w6w6w],[4w5w6w],[4w4w],[2w2w2w],听6w
正常胡牌: [7w8w9w],[6w6w],[5w6w7w],[4w4w4w],[2w2w2w],听7w

有关四川麻将判断胡牌,找到要听的牌的更多相关文章

  1. ruby-on-rails - capybara ::ElementNotFound:无法找到 xpath "/html" - 2

    我正在学习http://ruby.railstutorial.org/chapters/static-pages上的RubyonRails教程并遇到以下错误StaticPagesHomepageshouldhavethecontent'SampleApp'Failure/Error:page.shouldhave_content('SampleApp')Capybara::ElementNotFound:Unabletofindxpath"/html"#(eval):2:in`text'#./spec/requests/static_pages_spec.rb:7:in`(root)'

  2. ruby - 如何找到调用当前方法的方法 - 2

    如何找到调用此方法的位置?defto_xml(options={})binding.pryoptions=options.to_hifoptions&&options.respond_to?(:to_h)serializable_hash(options).to_xml(options)end 最佳答案 键入caller。这将返回当前调用堆栈。文档:Kernel#caller.例子[0]%rspecspec10/16|===================================================62=====

  3. python - 帮我找到合适的 ruby​​/python 解析器生成器 - 2

    我使用的第一个解析器生成器是Parse::RecDescent,它的指南/教程很棒,但它最有用的功能是它的调试工具,特别是tracing功能(通过将$RD_TRACE设置为1来激活)。我正在寻找可以帮助您调试其规则的解析器生成器。问题是,它必须用python或ruby​​编写,并且具有详细模式/跟踪模式或非常有用的调试技术。有人知道这样的解析器生成器吗?编辑:当我说调试时,我并不是指调试python或ruby​​。我指的是调试解析器生成器,查看它在每一步都在做什么,查看它正在读取的每个字符,它试图匹配的规则。希望你明白这一点。赏金编辑:要赢得赏金,请展示一个解析器生成器框架,并说明它的

  4. ruby - 404 未找到,但可以从网络浏览器正常访问 - 2

    我在这方面尝试了很多URL,在我遇到这个特定的之前,它们似乎都很好:require'rubygems'require'nokogiri'require'open-uri'doc=Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html"))putsdoc这是结果:/Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:353:in`open_http':404NotFound(OpenURI::HT

  5. ruby-on-rails - 通过 has_many 找到 Rails :through - 2

    我正在寻找一种方法来通过关联查询基于has_many中的子项的模型。我有3个模型:classConversation我需要找到参与者匹配一组ID的对话。这是我目前拥有的(不工作):Conversation.includes(:participants).where(participants:params[:participants]) 最佳答案 听起来你只是想要对话,如果是这样你可以加入。Conversation.joins(:participants).where(:users=>{:id=>params[:participant

  6. ruby - capybara 无法通过 id 找到元素 - 2

    capybara找不到在我的cucumber测试中用它的id标记。当我save_and_open_page时,我能够看到该元素.但我无法通过has_css?找到它或find:pry(#)>page.html.scan(/notice_sent/).count=>1pry(#)>page.html.scan(/id=\"notice_sent\"/).count=>1pry(#)>page.find('#notice_sent')Capybara::ElementNotFound:Unabletofindcss"#notice_sent"from/Users/me/.gem/ruby/2

  7. ruby-on-rails - Ruby 如何知道在哪里可以找到所需的文件? - 2

    这里还有一个新手问题:require'tasks/rails'我在每个Rails项目的根路径中的Rakefile中看到了这一行。我猜这行用于要求vendor/rails/railties/lib/tasks/rails.rb加载所有rake任务:$VERBOSE=nil#LoadRailsrakefileextensionsDir["#{File.dirname(__FILE__)}/*.rake"].each{|ext|loadext}#LoadanycustomrakefileextensionsDir["#{RAILS_ROOT}/lib/tasks/**/*.rake"].so

  8. ruby - 如何找到我的 Ruby 应用程序中的性能瓶颈? - 2

    我编写了一个Ruby应用程序,它可以解析来自不同格式html、xml和csv文件的源中的大量数据。我如何找出代码的哪些区域花费的时间最长?有没有关于如何提高Ruby应用程序性能的好资源?或者您是否有任何始终遵循的性能编码标准?例如,你总是用加入你的字符串吗?output=String.newoutput或者你会使用output="#{part_one}#{part_two}\n" 最佳答案 好吧,有一些众所周知的做法,例如字符串连接比“#{value}”慢得多,但是为了找出您的脚本在哪里消耗了大部分时间或比所需时间更多,您需要进行分

  9. 机器学习——时间序列ARIMA模型(四):自相关函数ACF和偏自相关函数PACF用于判断ARIMA模型中p、q参数取值 - 2

    文章目录1、自相关函数ACF2、偏自相关函数PACF3、ARIMA(p,d,q)的阶数判断4、代码实现1、引入所需依赖2、数据读取与处理3、一阶差分与绘图4、ACF5、PACF1、自相关函数ACF自相关函数反映了同一序列在不同时序的取值之间的相关性。公式:ACF(k)=ρk=Cov(yt,yt−k)Var(yt)ACF(k)=\rho_{k}=\frac{Cov(y_{t},y_{t-k})}{Var(y_{t})}ACF(k)=ρk​=Var(yt​)Cov(yt​,yt−k​)​其中分子用于求协方差矩阵,分母用于计算样本方差。求出的ACF值为[-1,1]。但对于一个平稳的AR模型,求出其滞

  10. 如何判断oracle是否启动及启动oracle数据库 - 2

    plsql连接Oracle超时,完犊子了肯定是服务器断电了。得马上检查Oracle服务器状态1、检查数据库是否启动su-oracle切换到Oracle用户,输入sqlplus/assysdba显示连接状态。如果末尾显示的状态是Connectedtoanidleinstance.证明未启动2、启动数据库startup启动数据库,末尾出现Databaseopened说明数据库启动成功3、查看数据库监听是否正常先quit;断开Oracle连接,使用lsnrctlstatus查看监听状态,如果出现TNS-开头的Nolistener、Connectionrefused等错误,说明监听未启动4、启动数据库

随机推荐