game_sync/gamerule/samloc/combine.go

1379 lines
27 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package samloc
import (
"errors"
"sort"
)
type line struct {
min int //最小牌
num int // 主牌 333
len int // 主牌数量 333444 -->2
tks []*line
}
func (l *line) incr(m map[int]int) {
for i := l.min; i < l.min+l.len; i++ {
m[i] += l.num
}
}
func (l *line) decr(m map[int]int) {
for i := l.min; i < l.min+l.len; i++ {
m[i] -= l.num
}
}
func (l *line) deepEqual(r *line) bool {
// 地址相等必相同
if l == r {
return true
}
if l.min != r.min || l.num != r.num || l.len != r.len {
return false
}
if len(l.tks) != len(r.tks) {
return false
}
for i := 0; i < len(l.tks); i++ {
if !l.tks[i].deepEqual(r.tks[i]) {
return false
}
}
return true
}
// 权重
func (l *line) Type() int {
switch l.num {
case 4:
// 四张
if len(l.tks) != 0 {
// 双四张
return TwinFour
}
return Four
case 3:
// 三牌
return Triple
case 2:
// 对牌
return Twin
case 1:
if l.len >= 3 {
// 单连
return Straight
}
// 单牌
return Single
}
return 0
}
// 三牌四牌带牌
func (l *line) canTake() int {
if l.tks != nil {
return 0
}
/*if l.num == 4 {
return 2
} else if l.num == 3 {
return l.len
}*/
return 0
}
// 能否出牌
func (l *line) canOut(pL int) bool {
return true
}
func (l *line) isTake(b int) bool {
for i := 0; i < l.len; i++ {
if b == l.min+i {
return false
}
}
return true
}
func (l *line) hintType() int {
switch l.num {
case 4:
if len(l.tks) == 0 {
if l.len == 2 {
// 双四张
return TwinFour
}
return Four
}
// return Pass
case 3:
if len(l.tks) == 0 {
return Triple
}
// return Pass
case 2:
if l.len == 1 {
return Twin
}
// return Pass
case 1:
if l.len == 1 {
return Single
}
if l.len >= 3 {
// 单连
return Straight
}
}
return Pass
}
func (l *line) hintLen() (n int) {
for _, v := range l.tks {
n += v.hintLen()
}
n += int(l.num * l.len)
return
}
// l > o 不同类型小于等于为false 大于true
func (l *line) isBattle(o *line) bool {
tp := l.hintType()
ts := o.hintType()
tpl := l.hintLen()
tsl := o.hintLen()
if tp == ts {
if tpl == tsl {
return l.min > o.min
}
return false
}
if o.min == int(POKER_2) {
if ts == Single && tp == Four { // 0
return true
} else if ts == Twin && tp == TwinFour {
return true
}
}
return false
}
func (l *line) isBigThan(lastShowCard *KindOfCard) bool {
if lastShowCard == nil {
return true
}
tp := l.hintType()
if tp == lastShowCard.GetKind() {
if l.hintLen() == int(lastShowCard.GetLen()*lastShowCard.GetSame()) { // 同类型,同数量
return l.min > int(lastShowCard.GetMin())
}
return false
}
if lastShowCard.GetMin() == POKER_2 {
if lastShowCard.GetKind() == Single && tp == Four {
return true
} else if lastShowCard.GetKind() == Twin && tp == TwinFour {
return true
}
}
return false
}
func (l *line) outType(m int) int {
if l.len > 1 || (l.len > 1 && l.num == 3) {
if l.tks != nil && l.min <= m+0x8 {
return 5 // 飞机带
}
if l.min <= m+0x8-l.num*2 {
return 4 // 成连
}
}
if l.num > 2 && l.min <= m {
return 3 // 可带
}
for _, v := range l.tks {
if v.outType(m) == 1 {
return 2 // 被带
}
}
if l.min <= m {
return 1 // 普通
}
return 0
}
func (l *line) firstTake() *line {
for _, v := range l.tks {
return v // 最小的
}
return nil
}
/*
func (l *line) Print(w io.Writer) {
var trans = map[int]string{
0: "3",
1: "4",
2: "5",
3: "6",
4: "7",
5: "8",
6: "9",
7: "10",
8: "J",
9: "Q",
10: "K",
11: "A",
12: "2",
}
l.nestPrint(w, trans, "")
if l != nil {
for _, v := range l.tks {
v.nestPrint(w, trans, " 带")
}
}
// fmt.Fprintln(w)
}
func (l *line) nestPrint(w io.Writer, trans map[int]string, take string) {
if l != nil {
switch l.Type() {
case TwinFour:
fmt.Fprintf(w, "两组四牌 ")
case Triple:
fmt.Fprint(w, "三牌: ")
case Four:
fmt.Fprint(w, "四牌: ")
case Straight:
fmt.Fprintf(w, "%d单连: ", l.len)
case Twin:
fmt.Fprintf(w, "%s对牌: ", take)
case Single:
fmt.Fprintf(w, "%s单牌: ", take)
}
for i := l.min; i < l.min+l.len; i++ {
fmt.Fprint(w, trans[i], ", ")
}
} else {
fmt.Fprintf(w, "没有找到该出手牌 ")
}
}
*/
func (l *line) outPokers(poker []int32) ([]int32, []int32) {
pok := make([]int32, len(poker))
copy(pok, poker)
var ret []int32
if l != nil {
for i := l.min; i != l.min+l.len; i++ {
for j := 0; j != l.num; j++ {
for k, v := range pok {
if realVle(v) == i {
ret = append(ret, v)
pok = append(pok[:k], pok[k+1:]...) // del
break
}
}
}
}
for _, ll := range l.tks {
for i := ll.min; i != ll.min+ll.len; i++ {
for j := 0; j != ll.num; j++ {
for k, v := range pok {
if realVle(v) == i {
ret = append(ret, v)
pok = append(pok[:k], pok[k+1:]...) // del
break
}
}
}
}
}
}
// fmt.Println(poker, pok)
return ret, pok
}
type cutex struct {
l []*line
h int // 手数
adv bool // 2属于绝对优势牌 h <1 先出2
b int // 炸弹
m []int // 哪几手非最大方便测试人员后在selectOut用于确认是否最大
f bool // 四带标志(炸弹带)
}
func (c *cutex) takeOther(max int) bool {
var n int
for k := len(c.l) - 1; k >= 0; k-- {
if c.l[k].len != 1 || c.l[k].num > 2 {
break
}
if c.l[k].min >= max {
continue
}
if n++; n >= 2 {
return true
}
}
return false
}
/*
通过他人手牌推算出目前自己本手手牌是否为最大,最大不计入手数
*/
type enemy struct {
otherPokers [][]int32
}
func newEnemy(pokers [][]int32) *enemy {
var e enemy
e.otherPokers = make([][]int32, len(pokers))
for i, v := range pokers {
e.otherPokers[i] = append(e.otherPokers[i], v...)
}
return &e
}
func (e *enemy) isMax(ls *line) bool {
for _, v := range e.otherPokers {
m := ptm(v)
lines := findSameNum(m, 4)
tp := ls.hintType()
if len(v) < ls.hintLen() {
continue
}
switch tp {
case Straight:
lines = findPlane(m, 1, ls.num)
for _, v1 := range lines {
if v1.isBattle(ls) {
return false
}
}
case Triple:
lines = findSameNum(m, 3)
for _, v1 := range lines {
if v1.isBattle(ls) {
return false
}
}
case Four:
lines = findSameNum(m, 4)
for _, v1 := range lines {
if v1.isBattle(ls) {
return false
}
}
default:
if ls.min == int(POKER_2) {
if tp == Twin {
lines = findTwoSameNum(m, 4, 2)
for _, v1 := range lines {
if v1.isBattle(ls) {
return false
}
}
} else if tp == Single {
lines = findSameNum(m, 4)
for _, v1 := range lines {
if v1.isBattle(ls) {
return false
}
}
}
} else {
lines = findSameType(m, ls.num)
for _, v1 := range lines {
if v1.isBattle(ls) {
return false
}
}
}
}
}
return true
}
type cutexSlice []*cutex
func (c cutexSlice) Len() int {
return len(c)
}
func (c cutexSlice) Less(i, j int) bool {
if c[i].h != c[j].h {
return c[i].h < c[j].h // 少->多
}
if c[i].b != c[j].b {
return c[i].b > c[j].b // 8888 7 6 全场最大时不带着出
}
return len(c[i].l) < len(c[j].l) // 888 7 6 全场最大时带着出
}
func (c cutexSlice) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
func realVle(v int32) int { // 0-12 3~ A 2
return int(v) % PER_CARD_COLOR_MAX
}
func ptm(in []int32) map[int]int {
m := make(map[int]int)
for _, v := range in {
m[realVle(v)]++
}
return m
}
func haveLine(m map[int]int, num int, len int, min int) bool {
if min+len > int(POKER_2) { // 2不能带
return false
}
for i := min; i < min+len; i++ {
if m[i] < num {
return false
}
}
return true
}
func makeLine(m, n, l int, s ...*line) *line {
return &line{
min: m, // 最小牌值
num: n, // 单张数量
len: l, // 长度
tks: s,
}
}
// 寻找相同个数的
func findSameNum(m map[int]int, n int) (res []*line) {
maxcard := int(POKER_2)
for i := 0; i <= maxcard; i++ {
if m[i] >= n {
res = append(res, makeLine(i, m[i], 1))
res[len(res)-1].decr(m)
}
}
return
}
// 寻找两4张
func findTwoSameNum(m map[int]int, n int, l int) (res []*line) {
re := makeLine(-1, -1, -1)
// re = makeLine(i, m[i], 1)
maxcard := int(POKER_2)
for i := 0; i <= maxcard; i++ {
if m[i] >= n {
if re.min == -1 {
re.min = i
re.num = m[i]
re.len = 1
} else {
re.tks = append(re.tks, makeLine(i, m[i], l))
// re.max = i
re.len++
}
if re.len == l {
res = append(res, re)
return
}
}
}
return
}
// 连牌
func findPlane(m map[int]int, num int, len int) (res []*line) {
for i := len; ; i++ {
var flag bool // 默认false 单联判断成功就继续
for j := 0; j <= 11; j++ {
if haveLine(m, num, i, j) {
res = append(res, makeLine(j, num, i))
flag = true
}
}
if !flag {
break
}
}
return
}
func findCommon(m map[int]int, n int) (res []*line) {
max := int(POKER_2)
for i := 0; i <= max; i++ {
if m[i] >= n {
res = append(res, makeLine(i, m[i], 1))
}
}
return
}
func findSame(m map[int]int, n int) (res []*line) {
max := int(POKER_2)
for i := 0; i <= max; i++ {
if m[i] >= n {
res = append(res, makeLine(i, n, 1))
}
}
return
}
func findSameType(m map[int]int, n int) (res []*line) {
maxcard := int(POKER_2)
for i := 0; i <= maxcard; i++ {
if m[i] >= n {
res = append(res, makeLine(i, n, 1))
}
}
return
}
// 遍历选定某连,找出与该连能同时存在的连,递归以上操作直至找不到,这些选定的连则是一种合法选择
// 请给参数t指向的切片足够的容量如此以来在递归调用中将不会因扩展容量而重新申请内存
// 可以把递归想象成前序遍历树由根向左t用来记录由根到当前的所有节点
// 参数l和局部变量ll用来表示当前节点的子节点
// 参数m用来限制子节点的生成
// 参数d用来记录深度
func selectLine(m map[int]int, l []*line, t []*line, s *[][]*line, d int) {
for _, v := range l {
t = append(t[:d], v)
v.decr(m)
var ll []*line
for _, vv := range l {
// 不考虑自己增加为压而拆后这里存在再找自己有效的情况譬如对2被拆v是单2
if v == vv {
continue
}
if haveLine(m, vv.num, vv.len, vv.min) {
ll = append(ll, vv) // 能匹配的向来不多这里不事先make
}
}
if len(ll) == 0 {
// 必须拷贝出来因为参数t随着递归会变前个合法选择会被下个覆盖
tt := make([]*line, 0, len(t))
tt = append(tt, t...)
*s = append(*s, tt)
} else {
selectLine(m, ll, t, s, d+1)
}
v.incr(m)
}
}
// 0 相等, 1 大于, -1 小于
type compareFunc func(p, q *line) int
type multiSorter struct {
ls []*line
fs []compareFunc
}
func (ms *multiSorter) Sort(ls []*line) {
ms.ls = ls
sort.Sort(ms)
}
func (ms *multiSorter) Len() int {
return len(ms.ls)
}
func (ms *multiSorter) Swap(i, j int) {
ms.ls[i], ms.ls[j] = ms.ls[j], ms.ls[i]
}
// 小 -> 大
func (ms *multiSorter) Less(i, j int) bool {
for k := 0; k < len(ms.fs); k++ {
if n := ms.fs[k](ms.ls[i], ms.ls[j]); n == 1 {
return false
} else if n == -1 {
return true
}
}
return false
}
func orderedBy(fs ...compareFunc) *multiSorter {
return &multiSorter{
fs: fs,
}
}
// selectLine 后很容易[33 44][44 33]重复
// 但[33]均属于相同实例,排序后可以通过地址快速判定是否相同
// combineTake 后很容易带与被带均相同但由于带牌是拷贝而地址不等
// 必要时可为 selectLine 后单独实现 fastEqualLine 里面仅比较地址
func equalLine(l []*line, r []*line) bool {
if len(l) != len(r) {
return false
}
for i := 0; i < len(l); i++ {
if !l[i].deepEqual(r[i]) {
return false
}
}
return true
}
func distinctLine(s [][]*line) [][]*line {
// 排序便于比较相同
for _, v := range s {
orderedBy(rSortType, rSortMin, rSortLen).Sort(v)
}
//删除第二层重复的
for p, lines := range s {
arr_len := len(lines)
for i := 0; i < arr_len; i++ {
for j := i + 1; j < arr_len; j++ {
l := lines[i]
r := lines[j]
if l.min != r.min || l.num != r.num || l.len != r.len {
continue
}
s[p] = append(s[p][:i], lines[i+1:]...)
arr_len--
i--
break
}
}
}
//删除第一层重复的
arr_len := len(s)
for i := 0; i < arr_len; i++ {
for j := i + 1; j < arr_len; j++ {
l := s[i]
r := s[j]
CompareSlice := func(l, r []*line) bool {
if len(l) != len(r) {
return false
}
var n int
for i := 0; i < len(l); i++ {
for j := 0; j < len(r); j++ {
if l[i].min != r[j].min || l[i].num != r[j].num || l[i].len != r[j].len {
n++
break
}
}
}
if n == len(r) {
return true
}
return false
}
if CompareSlice(l, r) {
continue
}
s = append(s[:i], s[i+1:]...)
arr_len--
i--
break
}
}
//for i := 0; i < len(s); i++ {
// for j := i + 1; j < len(s); j++ {
// if equalLine(s[i], s[j]) {
// s = append(s[:j], s[j+1:]...)
// j--
// }
// }
//}
return s
}
func moreSplitTake(s *[][]*line, pL int, lastShowCard *KindOfCard) {
ls := len(*s)
for i := 0; i < ls; i++ {
var ct int // 可带牌数量
p := (*s)[i] // 始终指向即将拆对的
pos := len(p) // 初始位置为长度预防无单对
for k, v := range p {
ct += v.canTake()
if pos == len(p) && v.len == 1 && v.num <= 2 {
pos = k // 记录首个单对
}
}
// 单对不分类型按大小排序
orderedBy(rSortMin).Sort(p[pos:])
var bt int // 被带牌数量
var t []int // 记录对牌位置
for j := len(p) - 1; j >= 0; j-- {
if p[j].len != 1 || p[j].num > 2 {
break
}
if bt++; bt >= ct {
break
}
if p[j].num == 2 {
t = append(t, j)
bt++ // 对牌充当两单牌
}
}
// 从小到大拆对
for _, v := range t {
n := make([]*line, 0, len(p)+1)
l := makeLine(p[v].min, 1, 1)
n = append(n, p[:v]...)
n = append(n, p[v+1:]...)
n = append(n, l, l)
// 新增一种切牌
*s = append(*s, n)
orderedBy(rSortType, rSortMin, rSortLen).Sort(p[pos:])
p = n // 下次以本次结果为基础
}
orderedBy(rSortType, rSortMin, rSortLen).Sort(p[pos:])
}
}
func combineTake(s *[][]*line, n int, pL int, lastShowCard *KindOfCard) {
ln := len(*s)
for i := n; i < ln; i++ {
for j := 0; j < len((*s)[i]); j++ {
// 已没有可带牌
if (*s)[i][j].num < 3 {
break
}
// 此牌已带过
ct := (*s)[i][j].canTake()
// fmt.Println("Here", ct)
if ct == 0 {
continue
}
// 记录被带单或对的位置
is := make([]int, 0, ct)
id := make([]int, 0, ct)
for k := len((*s)[i]) - 1; k >= 0; k-- {
// 已没有单或对
if (*s)[i][k].len != 1 || (*s)[i][k].num > 2 {
break
}
// 校验带牌与被带牌不能相同
if !(*s)[i][j].isTake((*s)[i][k].min) {
continue
}
if (*s)[i][k].num == 1 && len(is) < int(ct) {
is = append(is, k)
}
if (*s)[i][k].num == 2 && len(id) < int(ct) {
id = append(id, k)
}
// 已找够单和对
if len(is) == ct || len(id) == ct {
break
}
}
// fmt.Println("Here", ct, is, id)
if len(is) == ct {
*s = append(*s, pickupTake((*s)[i], j, is))
}
if len(id) == ct {
*s = append(*s, pickupTake((*s)[i], j, id))
}
}
}
// 有新增则递归
if len(*s) > ln {
combineTake(s, ln, pL, lastShowCard)
}
}
// 带与被带之外为复制需考虑四点
// [#1][带][#2][被带2][#3][被带1][#4]
// 为便于理解刻意举需要考虑#3的例子
// [KKKAAA][555666][9][8][6][3]
// 当切成这样并考虑为56带38时
// m = 1; t = [53]
func pickupTake(s []*line, m int, t []int) []*line {
r := make([]*line, 0, len(s)-len(t))
r = append(r, s[:m]...) // #1
tks := make([]*line, 0, len(t))
for _, v := range t {
tks = append(tks, s[v])
}
r = append(r, makeLine(s[m].min, s[m].num, s[m].len, tks...))
for i := len(t) - 1; i >= 0; i-- {
if i == len(t)-1 {
r = append(r, s[m+1:t[i]]...) // #2
} else {
r = append(r, s[t[i+1]+1:t[i]]...) // #3
}
}
return append(r, s[t[0]+1:]...) // #4
}
func splitThree(lines []*line) bool {
min := -2
flag := false
for _, v := range lines {
if v.num != 3 || v.min == 11 {
flag = false
break
} else {
if min == -2 {
min = v.min
} else if !flag && v.min-1 != min && v.min+1 != min { // 不是飞机的三带
flag = true
}
}
}
return flag
}
// cutPokers pokers 自己手牌 lastShowCard 上家出牌信息 otherPokers 别人剩余手牌
func cutPokers(pokers []int32, lastShowCard *KindOfCard, otherPokers [][]int32) []*cutex {
m := ptm(pokers)
pLen := len(pokers)
var lines []*line
// line 代表该名玩家能组成的所有牌型
lines = append(lines, findPlane(m, 1, 3)...) // 单连, 未去重
lines = append(lines, findCommon(m, 2)...) // 四牌 三牌 对牌
lines = append(lines, findPress(m, lastShowCard)...) // 如果上家有牌就拆开
var s [][]*line
t := make([]*line, 0, len(lines))
selectLine(m, lines, t, &s, 0)
// 去重
s = distinctLine(s)
for i := 0; i < len(s); i++ {
n := len(s[i])
for j := 0; j < n; j++ {
s[i][j].decr(m)
}
// 拆四余三、拆三余对、拆对余单、原本的单牌
s[i] = append(s[i], findCommon(m, 1)...)
for j := 0; j < n; j++ {
s[i][j].incr(m)
}
orderedBy(rSortType, rSortMin, rSortLen).Sort(s[i])
}
// 只有散牌
if len(s) == 0 {
s = append(s, findCommon(m, 1))
s = distinctLine(s)
} else {
moreSplitTake(&s, pLen, lastShowCard)
// 带牌
combineTake(&s, 0, pLen, lastShowCard)
s = distinctLine(s)
}
cs := make([]*cutex, 0, len(s))
enemy := newEnemy(otherPokers)
var isMaxFlag bool
if /*lastShowCard != nil ||*/ len(otherPokers) == 0 { // 由于首出牌敌人手牌传入为空,不限制会出错
isMaxFlag = true
}
for _, v := range s {
c := &cutex{
l: v,
h: len(v),
}
if m[int(POKER_2)] != 0 {
c.adv = true
}
for k, vv := range v {
if !isMaxFlag && enemy.isMax(vv) {
if lastShowCard != nil && vv.isBigThan(lastShowCard) {
c.h--
continue
} else if lastShowCard == nil {
c.h--
continue
}
} else if vv.min == int(POKER_2) {
c.h--
continue
}
c.m = append(c.m, k)
continue
}
cs = append(cs, c)
}
sort.Sort(cutexSlice(cs))
return cs
}
func headHintOut(pokers []int32, lastShowCard *KindOfCard) (lines []*line, retErr error) { // 提示
defer func() { // 增加容错处理
if err := recover(); err != nil {
retErr = errors.New("headHintOut is err")
lines = nil
}
}()
if lastShowCard != nil && lastShowCard.len == 0 {
//logger.Logger.Tracef("lastShowCard num is zero %v", lastShowCard)
lastShowCard = nil
}
//var lines []*line
m := ptm(pokers)
lines = append(lines, findPlane(m, 1, 3)...) // 单连, 未去重
lines = append(lines, findSame(m, 4)...) // 四牌
lines = append(lines, findSame(m, 3)...) // 三牌
lines = append(lines, findSame(m, 2)...) // 对牌
lines = append(lines, findSame(m, 1)...) // 单牌
return
}
/*
outType:
暂时考虑...
0 各自为战
1 给下个人喂牌,保证自己手数减少情况下管牌
2 管牌,保证自己手数情况下压牌
*/
func selectOut(pokers []int32, lastShowCard *KindOfCard, otherPokers [][]int32, warn bool, outType int) (ls *line, retErr error) { // 选择手牌
defer func() { // 增加容错处理
if err := recover(); err != nil {
retErr = errors.New("selectOut is err")
ls = nil
}
}()
if lastShowCard != nil && lastShowCard.len == 0 {
//logger.Logger.Tracef("lastShowCard num is zero %v", lastShowCard)
lastShowCard = nil
}
cs := cutPokers(pokers, lastShowCard, otherPokers)
/*for _, v := range cs {
for _, v1 := range v.l {
fmt.Printf("CS: min:%v num:%v len:%v\n", v1.min, v1.num, v1.len)
for _, v2 := range v1.tks {
fmt.Printf(" tks:%v ", *v2)
}
v1.Print(os.Stdout)
fmt.Println()
}
fmt.Printf(" cs h:%v b:%v m:%v abv:%v \n\n", v.h, v.b, v.m, v.adv)
}*/
// pL := len(pokers)
// var one *line
var offset int
// h==0 全是最大,找到直接出
// h==1 先找最大,并记录首个非最大,若没有最大则出非最大,此时会赢
for _, c := range cs {
if c.h > 1 { // 不能一手出完就跳过
break
}
for k, l := range c.l {
if !l.isBigThan(lastShowCard) {
continue
}
// 没有非最大或这个不是非最大,说明这个是最大 考虑如果剩下一手情况下
if len(c.m) == 0 || c.m[0] != k { // 后续考虑代码优化
if len(c.l) > 2 { //真实手数超过两手
for i, v := range c.l {
if !v.isBigThan(lastShowCard) {
continue
}
if len(c.m) == 0 {
if i == k {
continue
}
} else {
if i == k || i == c.m[0] {
continue
}
}
ls = v
// ls = calcOut(ls, pokers, lastShowCard, otherPokers)
return
}
}
ls = l
// ls = calcOut(ls, pokers, lastShowCard, otherPokers)
return
}
// 记录非最大
if ls == nil {
if outType == 0 { // 压牌
ls = calcOut(ls, pokers, lastShowCard, otherPokers, warn)
} else {
ls = l
}
}
}
offset++
}
// 出非最大
if ls != nil {
// ls = calcOut(ls, pokers, lastShowCard, otherPokers)
return //one, ret
}
if len(cs[offset:]) != 0 {
cs = cs[offset:]
}
// 首出
// var ls *line
if lastShowCard == nil {
ls, retErr = headOut(cs, pokers)
} else {
ls = pressOut(cs, lastShowCard, outType)
}
if outType == 0 { // 压牌
ls = calcOut(ls, pokers, lastShowCard, otherPokers, warn)
}
return //ls, ret //pressOut(cs, lastShowCard)
}
// 这里主要限制AAA带牌问题
func containBoom(ls []*line) bool {
if len(ls) == 0 {
return false
}
orderedBy(sortMin, sortTks).Sort(ls)
if ls[0].num == 3 && ls[0].min == 13 {
return true
}
return false
}
func pressOut(cs []*cutex, lastShowCard *KindOfCard, outType int) *line {
var h int
var ls []*line
// 首个能压手数中可出牌
// 本身就是根据h排序过的
for _, c := range cs {
if h != 0 && c.h > h {
/*if containBoom(ls) { // 判断管牌有没有包含
ls = ls[:0]
} else {*/
break // continue
//}
}
for _, l := range c.l {
if !l.isBigThan(lastShowCard) {
// fmt.Printf("cs: l%v type:%v\n", *l, l.hintType())
continue
}
ls = append(ls, l)
h = c.h
}
}
if len(ls) == 0 {
return nil
}
if outType != 0 {
if h > cs[0].h+2 {
return nil
}
}
/*// 无牌可出
if len(ls) == 0 {
return nil
}*/
// 小 -> 小带
orderedBy(sortMin, sortTks).Sort(ls)
return ls[0]
}
func calcOut(ls *line, pokers []int32, lastShowCard *KindOfCard, otherPokers [][]int32, warn bool) *line {
if (warn || len(otherPokers) != 0 && len(otherPokers[0]) == 1) && ls != nil { // 报单出最大
if ls.hintType() != Single {
return ls
}
m := ptm(pokers)
if lastShowCard == nil { // 自动出牌出对牌
for i := 0; i <= 11; i++ {
if m[i] == 2 {
return makeLine(i, m[i], 1)
}
}
}
// 没有对牌,出大单
maxcard := int(POKER_2)
for i := maxcard; i >= 0; i-- { // ls不为nil且为单牌 肯定能压牌
if m[i] != 0 {
return makeLine(i, 1, 1)
}
}
}
return ls
}
func headOut(cs []*cutex, pokers []int32) (l *line, retErr error) {
defer func() { // 增加容错处理
if err := recover(); err != nil {
retErr = errors.New("headOut is err")
l = nil
}
}()
tps := make(map[int][]*line)
// 最小逻辑牌值
min := int(POKER_2)
for _, v := range pokers {
if n := realVle(v); n < min {
min = n
}
}
// 从最少手数中选出携带最小牌值的出牌按牌型记录
for _, c := range cs {
if c.h > cs[0].h {
break
}
for _, l := range c.l {
t := l.outType(min) // 暂不修改
if t == 0 { // 不携带最小牌值
continue
}
if t == 2 && c.takeOther(l.min) {
// 最小牌属于被带且当前切牌有别的被带可选
tps[t] = append(tps[t], l.firstTake())
} else {
tps[t] = append(tps[t], l)
}
}
}
var i int
var ls []*line
// 飞机带 -> 成连 -> 可带 -> 被带 -> 普通
for i = 5; i > 0; i-- {
if v, ok := tps[i]; ok {
ls = v
break
}
}
lLen := len(ls)
if lLen == 0 { // 自动出牌判断出错,使用老处理逻辑重新判断
l = nil
retErr = errors.New("headOut is err")
return
}
switch i {
case 5:
// 小 -> 长 -> 小带
orderedBy(sortMin, rSortLen, sortTks).Sort(ls)
case 4:
// 小 -> 宽 -> 短
orderedBy(sortMin, rSortNum, sortLen).Sort(ls)
case 3:
// 小带
orderedBy(sortTks).Sort(ls)
case 2:
// 带 -> 小 -> 长
orderedBy(rSortTake, sortMin, rSortLen).Sort(ls)
case 1:
// 宽
orderedBy(rSortNum).Sort(ls)
}
l = ls[0]
//retErr = errors.New("headOut is err") // 返回错误,使用老处理逻辑
return // ls[10], ret
}
func findPress(m map[int]int, lastShowCard *KindOfCard) []*line {
if lastShowCard == nil {
return nil
}
n := 4
var ret []*line
switch lastShowCard.GetKind() {
case Single:
n = 1
if lastShowCard.GetMin() == POKER_2 {
n = 4
}
case Twin:
n = 2
if lastShowCard.GetMin() == POKER_2 {
n = 4
}
case Triple:
n = 3
case Four:
n = 4
}
for i := int(POKER_2); i > 0; i-- {
if i <= int(lastShowCard.GetMin()) {
break
}
if m[i] >= n {
ret = append(ret, makeLine(i, n, 1))
// return []*line{makeLine(i, n, 1)}
}
}
return ret
}
var rSortLen = func(p, q *line) int {
return sortLen(q, p)
}
var sortLen = func(p, q *line) int {
if p.len == q.len {
return 0
}
if p.len > q.len {
return 1
}
return -1
}
var rSortNum = func(p, q *line) int {
return sortNum(q, p)
}
var sortNum = func(p, q *line) int {
if p.num == q.num {
return 0
}
if p.num > q.num {
return 1
}
return -1
}
var rSortMin = func(p, q *line) int {
return sortMin(q, p)
}
var sortMin = func(p, q *line) int {
if p.min == q.min {
return 0
}
if p.min > q.min {
return 1
}
return -1
}
var rSortTks = func(p, q *line) int {
return sortTks(q, p)
}
var sortTks = func(p, q *line) int {
pL := len(p.tks)
qL := len(q.tks)
if pL != qL { // 带对牌跟两张单牌。只需考虑对牌需不需要拆开
if pL == 0 {
return 1
} else if qL == 0 {
return -1
}
if p.tks[0].min < q.tks[0].min {
return -1
}
if p.tks[0].min > q.tks[0].min {
return 1
}
if pL > qL {
return 1
}
return -1
}
for i := 0; i < pL; i++ {
if p.tks[i].min < q.tks[i].min {
return -1
}
if p.tks[i].min > q.tks[i].min {
return 1
}
}
return 0
}
var rSortType = func(p, q *line) int {
return sortType(q, p)
}
var sortType = func(p, q *line) int {
if tp, tq := p.Type(), q.Type(); tp == tq {
return 0
} else if tp > tq {
return 1
}
return -1
}
var rSortTake = func(p, q *line) int {
return sortTake(q, p)
}
var sortTake = func(p, q *line) int {
if p.tks != nil {
if q.tks == nil {
return 1
}
} else if q.tks != nil {
return -1
}
return 0
}