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 }