1379 lines
27 KiB
Go
1379 lines
27 KiB
Go
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
|
||
}
|