Browse Source

next round, finish settle game

master
tangmingyou 4 years ago
parent
commit
0b2f6b212a
  1. 4
      api/operation.go
  2. 180
      api/poker.pb.go
  3. 22
      api/poker.proto
  4. 500
      api/proto.js
  5. 206
      internal/game/hand.go
  6. 9
      internal/game/player.go
  7. 139
      internal/game/table.go
  8. 2
      internal/service/auth.go
  9. 191
      internal/service/table.go
  10. 20
      tool/collect/collect.go

4
api/operation.go

@ -47,9 +47,7 @@ var protoInstances = []proto.Message{
&ResDismissGameTable{}, &ResDismissGameTable{},
&ReqGameStart{}, &ReqGameStart{},
&ReqPlaceBetChip{}, &ReqPlaceBet{},
&ReqBetWeakHand{},
&ReqBetDiscard{},
&ResNoticePlayerLine{}, &ResNoticePlayerLine{},
} }

180
api/poker.pb.go

@ -1607,17 +1607,17 @@ func (*ResDismissGameTable) Descriptor() ([]byte, []int) {
} }
// 下注 // 下注
type ReqPlaceBetChip struct { type ReqPlaceBet struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
BetChip int32 `protobuf:"varint,1,opt,name=betChip,proto3" json:"betChip,omitempty"` // 跟注/加注金额 BetType int32 `protobuf:"varint,1,opt,name=betType,proto3" json:"betType,omitempty"` // 1跟注,2加注(-[0]+),3All-In,4弃牌,5过牌
AllIn bool `protobuf:"varint,2,opt,name=allIn,proto3" json:"allIn,omitempty"` // 是否 allIn BetChip int32 `protobuf:"varint,2,opt,name=betChip,proto3" json:"betChip,omitempty"` // 跟注/加注金额
} }
func (x *ReqPlaceBetChip) Reset() { func (x *ReqPlaceBet) Reset() {
*x = ReqPlaceBetChip{} *x = ReqPlaceBet{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_poker_proto_msgTypes[29] mi := &file_poker_proto_msgTypes[29]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@ -1625,13 +1625,13 @@ func (x *ReqPlaceBetChip) Reset() {
} }
} }
func (x *ReqPlaceBetChip) String() string { func (x *ReqPlaceBet) String() string {
return protoimpl.X.MessageStringOf(x) return protoimpl.X.MessageStringOf(x)
} }
func (*ReqPlaceBetChip) ProtoMessage() {} func (*ReqPlaceBet) ProtoMessage() {}
func (x *ReqPlaceBetChip) ProtoReflect() protoreflect.Message { func (x *ReqPlaceBet) ProtoReflect() protoreflect.Message {
mi := &file_poker_proto_msgTypes[29] mi := &file_poker_proto_msgTypes[29]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
@ -1643,101 +1643,23 @@ func (x *ReqPlaceBetChip) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x) return mi.MessageOf(x)
} }
// Deprecated: Use ReqPlaceBetChip.ProtoReflect.Descriptor instead. // Deprecated: Use ReqPlaceBet.ProtoReflect.Descriptor instead.
func (*ReqPlaceBetChip) Descriptor() ([]byte, []int) { func (*ReqPlaceBet) Descriptor() ([]byte, []int) {
return file_poker_proto_rawDescGZIP(), []int{29} return file_poker_proto_rawDescGZIP(), []int{29}
} }
func (x *ReqPlaceBetChip) GetBetChip() int32 { func (x *ReqPlaceBet) GetBetType() int32 {
if x != nil { if x != nil {
return x.BetChip return x.BetType
} }
return 0 return 0
} }
func (x *ReqPlaceBetChip) GetAllIn() bool { func (x *ReqPlaceBet) GetBetChip() int32 {
if x != nil { if x != nil {
return x.AllIn return x.BetChip
}
return false
}
// 让牌
type ReqBetWeakHand struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ReqBetWeakHand) Reset() {
*x = ReqBetWeakHand{}
if protoimpl.UnsafeEnabled {
mi := &file_poker_proto_msgTypes[30]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ReqBetWeakHand) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReqBetWeakHand) ProtoMessage() {}
func (x *ReqBetWeakHand) ProtoReflect() protoreflect.Message {
mi := &file_poker_proto_msgTypes[30]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ReqBetWeakHand.ProtoReflect.Descriptor instead.
func (*ReqBetWeakHand) Descriptor() ([]byte, []int) {
return file_poker_proto_rawDescGZIP(), []int{30}
}
// 弃牌
type ReqBetDiscard struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ReqBetDiscard) Reset() {
*x = ReqBetDiscard{}
if protoimpl.UnsafeEnabled {
mi := &file_poker_proto_msgTypes[31]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ReqBetDiscard) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReqBetDiscard) ProtoMessage() {}
func (x *ReqBetDiscard) ProtoReflect() protoreflect.Message {
mi := &file_poker_proto_msgTypes[31]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
} }
return mi.MessageOf(x) return 0
}
// Deprecated: Use ReqBetDiscard.ProtoReflect.Descriptor instead.
func (*ReqBetDiscard) Descriptor() ([]byte, []int) {
return file_poker_proto_rawDescGZIP(), []int{31}
} }
// 通知玩家某个其他玩家动作 // 通知玩家某个其他玩家动作
@ -1755,7 +1677,7 @@ type ResNoticePlayerLine struct {
func (x *ResNoticePlayerLine) Reset() { func (x *ResNoticePlayerLine) Reset() {
*x = ResNoticePlayerLine{} *x = ResNoticePlayerLine{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_poker_proto_msgTypes[32] mi := &file_poker_proto_msgTypes[30]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -1768,7 +1690,7 @@ func (x *ResNoticePlayerLine) String() string {
func (*ResNoticePlayerLine) ProtoMessage() {} func (*ResNoticePlayerLine) ProtoMessage() {}
func (x *ResNoticePlayerLine) ProtoReflect() protoreflect.Message { func (x *ResNoticePlayerLine) ProtoReflect() protoreflect.Message {
mi := &file_poker_proto_msgTypes[32] mi := &file_poker_proto_msgTypes[30]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -1781,7 +1703,7 @@ func (x *ResNoticePlayerLine) ProtoReflect() protoreflect.Message {
// Deprecated: Use ResNoticePlayerLine.ProtoReflect.Descriptor instead. // Deprecated: Use ResNoticePlayerLine.ProtoReflect.Descriptor instead.
func (*ResNoticePlayerLine) Descriptor() ([]byte, []int) { func (*ResNoticePlayerLine) Descriptor() ([]byte, []int) {
return file_poker_proto_rawDescGZIP(), []int{32} return file_poker_proto_rawDescGZIP(), []int{30}
} }
func (x *ResNoticePlayerLine) GetPlayerId() int64 { func (x *ResNoticePlayerLine) GetPlayerId() int64 {
@ -1936,22 +1858,20 @@ var file_poker_proto_rawDesc = []byte{
0x47, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x71, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x71,
0x44, 0x69, 0x73, 0x6d, 0x69, 0x73, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x69, 0x73, 0x6d, 0x69, 0x73, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65,
0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x44, 0x69, 0x73, 0x6d, 0x69, 0x73, 0x73, 0x47, 0x61, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x44, 0x69, 0x73, 0x6d, 0x69, 0x73, 0x73, 0x47, 0x61,
0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x41, 0x0a, 0x0f, 0x52, 0x65, 0x71, 0x50, 0x6c, 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x41, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x50, 0x6c,
0x61, 0x63, 0x65, 0x42, 0x65, 0x74, 0x43, 0x68, 0x69, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x65, 0x61, 0x63, 0x65, 0x42, 0x65, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x65, 0x74, 0x54, 0x79, 0x70,
0x74, 0x43, 0x68, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x62, 0x65, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x62, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65,
0x43, 0x68, 0x69, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x6c, 0x49, 0x6e, 0x18, 0x02, 0x20, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x65, 0x74, 0x43, 0x68, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28,
0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x6c, 0x49, 0x6e, 0x22, 0x10, 0x0a, 0x0e, 0x52, 0x65, 0x05, 0x52, 0x07, 0x62, 0x65, 0x74, 0x43, 0x68, 0x69, 0x70, 0x22, 0x73, 0x0a, 0x13, 0x52, 0x65,
0x71, 0x42, 0x65, 0x74, 0x57, 0x65, 0x61, 0x6b, 0x48, 0x61, 0x6e, 0x64, 0x22, 0x0f, 0x0a, 0x0d, 0x73, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4c, 0x69, 0x6e,
0x52, 0x65, 0x71, 0x42, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x22, 0x73, 0x0a, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20,
0x13, 0x52, 0x65, 0x73, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a,
0x4c, 0x69, 0x6e, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69,
0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x6e, 0x65, 0x31, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x18, 0x0c, 0x20, 0x01,
0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e,
0x05, 0x6c, 0x69, 0x6e, 0x65, 0x31, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x18, 0x65, 0x33, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x33, 0x42,
0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x12, 0x14, 0x0a, 0x05, 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x61, 0x70, 0x69, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72,
0x6c, 0x69, 0x6e, 0x65, 0x33, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x6f, 0x74, 0x6f, 0x33,
0x65, 0x33, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x61, 0x70, 0x69, 0x3b, 0x61, 0x70, 0x69, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -1966,7 +1886,7 @@ func file_poker_proto_rawDescGZIP() []byte {
return file_poker_proto_rawDescData return file_poker_proto_rawDescData
} }
var file_poker_proto_msgTypes = make([]protoimpl.MessageInfo, 33) var file_poker_proto_msgTypes = make([]protoimpl.MessageInfo, 31)
var file_poker_proto_goTypes = []interface{}{ var file_poker_proto_goTypes = []interface{}{
(*ProtoWrap)(nil), // 0: api.ProtoWrap (*ProtoWrap)(nil), // 0: api.ProtoWrap
(*Ping)(nil), // 1: api.Ping (*Ping)(nil), // 1: api.Ping
@ -1997,10 +1917,8 @@ var file_poker_proto_goTypes = []interface{}{
(*ReqGameStart)(nil), // 26: api.ReqGameStart (*ReqGameStart)(nil), // 26: api.ReqGameStart
(*ReqDismissGameTable)(nil), // 27: api.ReqDismissGameTable (*ReqDismissGameTable)(nil), // 27: api.ReqDismissGameTable
(*ResDismissGameTable)(nil), // 28: api.ResDismissGameTable (*ResDismissGameTable)(nil), // 28: api.ResDismissGameTable
(*ReqPlaceBetChip)(nil), // 29: api.ReqPlaceBetChip (*ReqPlaceBet)(nil), // 29: api.ReqPlaceBet
(*ReqBetWeakHand)(nil), // 30: api.ReqBetWeakHand (*ResNoticePlayerLine)(nil), // 30: api.ResNoticePlayerLine
(*ReqBetDiscard)(nil), // 31: api.ReqBetDiscard
(*ResNoticePlayerLine)(nil), // 32: api.ResNoticePlayerLine
} }
var file_poker_proto_depIdxs = []int32{ var file_poker_proto_depIdxs = []int32{
9, // 0: api.ResLobbyView.tables:type_name -> api.LobbyTable 9, // 0: api.ResLobbyView.tables:type_name -> api.LobbyTable
@ -2371,7 +2289,7 @@ func file_poker_proto_init() {
} }
} }
file_poker_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { file_poker_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ReqPlaceBetChip); i { switch v := v.(*ReqPlaceBet); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -2383,30 +2301,6 @@ func file_poker_proto_init() {
} }
} }
file_poker_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { file_poker_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ReqBetWeakHand); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_poker_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ReqBetDiscard); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_poker_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ResNoticePlayerLine); i { switch v := v.(*ResNoticePlayerLine); i {
case 0: case 0:
return &v.state return &v.state
@ -2425,7 +2319,7 @@ func file_poker_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_poker_proto_rawDesc, RawDescriptor: file_poker_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 33, NumMessages: 31,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },

22
api/poker.proto

@ -178,20 +178,9 @@ message ResDismissGameTable {
} }
// //
message ReqPlaceBetChip { message ReqPlaceBet {
int32 betChip = 1; // / int32 betType = 1; // 1,2(-[0]+),3All-In,4,5
bool allIn = 2; // allIn int32 betChip = 2; // /
}
//
message ReqBetWeakHand {
}
//
message ReqBetDiscard {
} }
// //
@ -202,3 +191,8 @@ message ResNoticePlayerLine {
string line2 = 12; string line2 = 12;
string line3 = 13; string line3 = 13;
} }
//
message ResCalcWinnerChip {
map<int64, int32> playersWinChip = 9;
}

500
api/proto.js

@ -6706,25 +6706,25 @@ export const api = $root.api = (() => {
return ResDismissGameTable; return ResDismissGameTable;
})(); })();
api.ReqPlaceBetChip = (function() { api.ReqPlaceBet = (function() {
/** /**
* Properties of a ReqPlaceBetChip. * Properties of a ReqPlaceBet.
* @memberof api * @memberof api
* @interface IReqPlaceBetChip * @interface IReqPlaceBet
* @property {number|null} [betChip] ReqPlaceBetChip betChip * @property {number|null} [betType] ReqPlaceBet betType
* @property {boolean|null} [allIn] ReqPlaceBetChip allIn * @property {number|null} [betChip] ReqPlaceBet betChip
*/ */
/** /**
* Constructs a new ReqPlaceBetChip. * Constructs a new ReqPlaceBet.
* @memberof api * @memberof api
* @classdesc Represents a ReqPlaceBetChip. * @classdesc Represents a ReqPlaceBet.
* @implements IReqPlaceBetChip * @implements IReqPlaceBet
* @constructor * @constructor
* @param {api.IReqPlaceBetChip=} [properties] Properties to set * @param {api.IReqPlaceBet=} [properties] Properties to set
*/ */
function ReqPlaceBetChip(properties) { function ReqPlaceBet(properties) {
if (properties) if (properties)
for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
if (properties[keys[i]] != null) if (properties[keys[i]] != null)
@ -6732,89 +6732,89 @@ export const api = $root.api = (() => {
} }
/** /**
* ReqPlaceBetChip betChip. * ReqPlaceBet betType.
* @member {number} betChip * @member {number} betType
* @memberof api.ReqPlaceBetChip * @memberof api.ReqPlaceBet
* @instance * @instance
*/ */
ReqPlaceBetChip.prototype.betChip = 0; ReqPlaceBet.prototype.betType = 0;
/** /**
* ReqPlaceBetChip allIn. * ReqPlaceBet betChip.
* @member {boolean} allIn * @member {number} betChip
* @memberof api.ReqPlaceBetChip * @memberof api.ReqPlaceBet
* @instance * @instance
*/ */
ReqPlaceBetChip.prototype.allIn = false; ReqPlaceBet.prototype.betChip = 0;
/** /**
* Creates a new ReqPlaceBetChip instance using the specified properties. * Creates a new ReqPlaceBet instance using the specified properties.
* @function create * @function create
* @memberof api.ReqPlaceBetChip * @memberof api.ReqPlaceBet
* @static * @static
* @param {api.IReqPlaceBetChip=} [properties] Properties to set * @param {api.IReqPlaceBet=} [properties] Properties to set
* @returns {api.ReqPlaceBetChip} ReqPlaceBetChip instance * @returns {api.ReqPlaceBet} ReqPlaceBet instance
*/ */
ReqPlaceBetChip.create = function create(properties) { ReqPlaceBet.create = function create(properties) {
return new ReqPlaceBetChip(properties); return new ReqPlaceBet(properties);
}; };
/** /**
* Encodes the specified ReqPlaceBetChip message. Does not implicitly {@link api.ReqPlaceBetChip.verify|verify} messages. * Encodes the specified ReqPlaceBet message. Does not implicitly {@link api.ReqPlaceBet.verify|verify} messages.
* @function encode * @function encode
* @memberof api.ReqPlaceBetChip * @memberof api.ReqPlaceBet
* @static * @static
* @param {api.IReqPlaceBetChip} message ReqPlaceBetChip message or plain object to encode * @param {api.IReqPlaceBet} message ReqPlaceBet message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to * @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer * @returns {$protobuf.Writer} Writer
*/ */
ReqPlaceBetChip.encode = function encode(message, writer) { ReqPlaceBet.encode = function encode(message, writer) {
if (!writer) if (!writer)
writer = $Writer.create(); writer = $Writer.create();
if (message.betType != null && Object.hasOwnProperty.call(message, "betType"))
writer.uint32(/* id 1, wireType 0 =*/8).int32(message.betType);
if (message.betChip != null && Object.hasOwnProperty.call(message, "betChip")) if (message.betChip != null && Object.hasOwnProperty.call(message, "betChip"))
writer.uint32(/* id 1, wireType 0 =*/8).int32(message.betChip); writer.uint32(/* id 2, wireType 0 =*/16).int32(message.betChip);
if (message.allIn != null && Object.hasOwnProperty.call(message, "allIn"))
writer.uint32(/* id 2, wireType 0 =*/16).bool(message.allIn);
return writer; return writer;
}; };
/** /**
* Encodes the specified ReqPlaceBetChip message, length delimited. Does not implicitly {@link api.ReqPlaceBetChip.verify|verify} messages. * Encodes the specified ReqPlaceBet message, length delimited. Does not implicitly {@link api.ReqPlaceBet.verify|verify} messages.
* @function encodeDelimited * @function encodeDelimited
* @memberof api.ReqPlaceBetChip * @memberof api.ReqPlaceBet
* @static * @static
* @param {api.IReqPlaceBetChip} message ReqPlaceBetChip message or plain object to encode * @param {api.IReqPlaceBet} message ReqPlaceBet message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to * @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer * @returns {$protobuf.Writer} Writer
*/ */
ReqPlaceBetChip.encodeDelimited = function encodeDelimited(message, writer) { ReqPlaceBet.encodeDelimited = function encodeDelimited(message, writer) {
return this.encode(message, writer).ldelim(); return this.encode(message, writer).ldelim();
}; };
/** /**
* Decodes a ReqPlaceBetChip message from the specified reader or buffer. * Decodes a ReqPlaceBet message from the specified reader or buffer.
* @function decode * @function decode
* @memberof api.ReqPlaceBetChip * @memberof api.ReqPlaceBet
* @static * @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @param {number} [length] Message length if known beforehand * @param {number} [length] Message length if known beforehand
* @returns {api.ReqPlaceBetChip} ReqPlaceBetChip * @returns {api.ReqPlaceBet} ReqPlaceBet
* @throws {Error} If the payload is not a reader or valid buffer * @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing * @throws {$protobuf.util.ProtocolError} If required fields are missing
*/ */
ReqPlaceBetChip.decode = function decode(reader, length) { ReqPlaceBet.decode = function decode(reader, length) {
if (!(reader instanceof $Reader)) if (!(reader instanceof $Reader))
reader = $Reader.create(reader); reader = $Reader.create(reader);
let end = length === undefined ? reader.len : reader.pos + length, message = new $root.api.ReqPlaceBetChip(); let end = length === undefined ? reader.len : reader.pos + length, message = new $root.api.ReqPlaceBet();
while (reader.pos < end) { while (reader.pos < end) {
let tag = reader.uint32(); let tag = reader.uint32();
switch (tag >>> 3) { switch (tag >>> 3) {
case 1: { case 1: {
message.betChip = reader.int32(); message.betType = reader.int32();
break; break;
} }
case 2: { case 2: {
message.allIn = reader.bool(); message.betChip = reader.int32();
break; break;
} }
default: default:
@ -6826,461 +6826,111 @@ export const api = $root.api = (() => {
}; };
/** /**
* Decodes a ReqPlaceBetChip message from the specified reader or buffer, length delimited. * Decodes a ReqPlaceBet message from the specified reader or buffer, length delimited.
* @function decodeDelimited * @function decodeDelimited
* @memberof api.ReqPlaceBetChip * @memberof api.ReqPlaceBet
* @static * @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @returns {api.ReqPlaceBetChip} ReqPlaceBetChip * @returns {api.ReqPlaceBet} ReqPlaceBet
* @throws {Error} If the payload is not a reader or valid buffer * @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing * @throws {$protobuf.util.ProtocolError} If required fields are missing
*/ */
ReqPlaceBetChip.decodeDelimited = function decodeDelimited(reader) { ReqPlaceBet.decodeDelimited = function decodeDelimited(reader) {
if (!(reader instanceof $Reader)) if (!(reader instanceof $Reader))
reader = new $Reader(reader); reader = new $Reader(reader);
return this.decode(reader, reader.uint32()); return this.decode(reader, reader.uint32());
}; };
/** /**
* Verifies a ReqPlaceBetChip message. * Verifies a ReqPlaceBet message.
* @function verify * @function verify
* @memberof api.ReqPlaceBetChip * @memberof api.ReqPlaceBet
* @static * @static
* @param {Object.<string,*>} message Plain object to verify * @param {Object.<string,*>} message Plain object to verify
* @returns {string|null} `null` if valid, otherwise the reason why it is not * @returns {string|null} `null` if valid, otherwise the reason why it is not
*/ */
ReqPlaceBetChip.verify = function verify(message) { ReqPlaceBet.verify = function verify(message) {
if (typeof message !== "object" || message === null) if (typeof message !== "object" || message === null)
return "object expected"; return "object expected";
if (message.betType != null && message.hasOwnProperty("betType"))
if (!$util.isInteger(message.betType))
return "betType: integer expected";
if (message.betChip != null && message.hasOwnProperty("betChip")) if (message.betChip != null && message.hasOwnProperty("betChip"))
if (!$util.isInteger(message.betChip)) if (!$util.isInteger(message.betChip))
return "betChip: integer expected"; return "betChip: integer expected";
if (message.allIn != null && message.hasOwnProperty("allIn"))
if (typeof message.allIn !== "boolean")
return "allIn: boolean expected";
return null; return null;
}; };
/** /**
* Creates a ReqPlaceBetChip message from a plain object. Also converts values to their respective internal types. * Creates a ReqPlaceBet message from a plain object. Also converts values to their respective internal types.
* @function fromObject * @function fromObject
* @memberof api.ReqPlaceBetChip * @memberof api.ReqPlaceBet
* @static * @static
* @param {Object.<string,*>} object Plain object * @param {Object.<string,*>} object Plain object
* @returns {api.ReqPlaceBetChip} ReqPlaceBetChip * @returns {api.ReqPlaceBet} ReqPlaceBet
*/ */
ReqPlaceBetChip.fromObject = function fromObject(object) { ReqPlaceBet.fromObject = function fromObject(object) {
if (object instanceof $root.api.ReqPlaceBetChip) if (object instanceof $root.api.ReqPlaceBet)
return object; return object;
let message = new $root.api.ReqPlaceBetChip(); let message = new $root.api.ReqPlaceBet();
if (object.betType != null)
message.betType = object.betType | 0;
if (object.betChip != null) if (object.betChip != null)
message.betChip = object.betChip | 0; message.betChip = object.betChip | 0;
if (object.allIn != null)
message.allIn = Boolean(object.allIn);
return message; return message;
}; };
/** /**
* Creates a plain object from a ReqPlaceBetChip message. Also converts values to other types if specified. * Creates a plain object from a ReqPlaceBet message. Also converts values to other types if specified.
* @function toObject * @function toObject
* @memberof api.ReqPlaceBetChip * @memberof api.ReqPlaceBet
* @static * @static
* @param {api.ReqPlaceBetChip} message ReqPlaceBetChip * @param {api.ReqPlaceBet} message ReqPlaceBet
* @param {$protobuf.IConversionOptions} [options] Conversion options * @param {$protobuf.IConversionOptions} [options] Conversion options
* @returns {Object.<string,*>} Plain object * @returns {Object.<string,*>} Plain object
*/ */
ReqPlaceBetChip.toObject = function toObject(message, options) { ReqPlaceBet.toObject = function toObject(message, options) {
if (!options) if (!options)
options = {}; options = {};
let object = {}; let object = {};
if (options.defaults) { if (options.defaults) {
object.betType = 0;
object.betChip = 0; object.betChip = 0;
object.allIn = false;
} }
if (message.betType != null && message.hasOwnProperty("betType"))
object.betType = message.betType;
if (message.betChip != null && message.hasOwnProperty("betChip")) if (message.betChip != null && message.hasOwnProperty("betChip"))
object.betChip = message.betChip; object.betChip = message.betChip;
if (message.allIn != null && message.hasOwnProperty("allIn"))
object.allIn = message.allIn;
return object; return object;
}; };
/** /**
* Converts this ReqPlaceBetChip to JSON. * Converts this ReqPlaceBet to JSON.
* @function toJSON
* @memberof api.ReqPlaceBetChip
* @instance
* @returns {Object.<string,*>} JSON object
*/
ReqPlaceBetChip.prototype.toJSON = function toJSON() {
return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
};
/**
* Gets the default type url for ReqPlaceBetChip
* @function getTypeUrl
* @memberof api.ReqPlaceBetChip
* @static
* @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
* @returns {string} The default type url
*/
ReqPlaceBetChip.getTypeUrl = function getTypeUrl(typeUrlPrefix) {
if (typeUrlPrefix === undefined) {
typeUrlPrefix = "type.googleapis.com";
}
return typeUrlPrefix + "/api.ReqPlaceBetChip";
};
return ReqPlaceBetChip;
})();
api.ReqBetWeakHand = (function() {
/**
* Properties of a ReqBetWeakHand.
* @memberof api
* @interface IReqBetWeakHand
*/
/**
* Constructs a new ReqBetWeakHand.
* @memberof api
* @classdesc Represents a ReqBetWeakHand.
* @implements IReqBetWeakHand
* @constructor
* @param {api.IReqBetWeakHand=} [properties] Properties to set
*/
function ReqBetWeakHand(properties) {
if (properties)
for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
if (properties[keys[i]] != null)
this[keys[i]] = properties[keys[i]];
}
/**
* Creates a new ReqBetWeakHand instance using the specified properties.
* @function create
* @memberof api.ReqBetWeakHand
* @static
* @param {api.IReqBetWeakHand=} [properties] Properties to set
* @returns {api.ReqBetWeakHand} ReqBetWeakHand instance
*/
ReqBetWeakHand.create = function create(properties) {
return new ReqBetWeakHand(properties);
};
/**
* Encodes the specified ReqBetWeakHand message. Does not implicitly {@link api.ReqBetWeakHand.verify|verify} messages.
* @function encode
* @memberof api.ReqBetWeakHand
* @static
* @param {api.IReqBetWeakHand} message ReqBetWeakHand message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
ReqBetWeakHand.encode = function encode(message, writer) {
if (!writer)
writer = $Writer.create();
return writer;
};
/**
* Encodes the specified ReqBetWeakHand message, length delimited. Does not implicitly {@link api.ReqBetWeakHand.verify|verify} messages.
* @function encodeDelimited
* @memberof api.ReqBetWeakHand
* @static
* @param {api.IReqBetWeakHand} message ReqBetWeakHand message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
ReqBetWeakHand.encodeDelimited = function encodeDelimited(message, writer) {
return this.encode(message, writer).ldelim();
};
/**
* Decodes a ReqBetWeakHand message from the specified reader or buffer.
* @function decode
* @memberof api.ReqBetWeakHand
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @param {number} [length] Message length if known beforehand
* @returns {api.ReqBetWeakHand} ReqBetWeakHand
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
ReqBetWeakHand.decode = function decode(reader, length) {
if (!(reader instanceof $Reader))
reader = $Reader.create(reader);
let end = length === undefined ? reader.len : reader.pos + length, message = new $root.api.ReqBetWeakHand();
while (reader.pos < end) {
let tag = reader.uint32();
switch (tag >>> 3) {
default:
reader.skipType(tag & 7);
break;
}
}
return message;
};
/**
* Decodes a ReqBetWeakHand message from the specified reader or buffer, length delimited.
* @function decodeDelimited
* @memberof api.ReqBetWeakHand
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @returns {api.ReqBetWeakHand} ReqBetWeakHand
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
ReqBetWeakHand.decodeDelimited = function decodeDelimited(reader) {
if (!(reader instanceof $Reader))
reader = new $Reader(reader);
return this.decode(reader, reader.uint32());
};
/**
* Verifies a ReqBetWeakHand message.
* @function verify
* @memberof api.ReqBetWeakHand
* @static
* @param {Object.<string,*>} message Plain object to verify
* @returns {string|null} `null` if valid, otherwise the reason why it is not
*/
ReqBetWeakHand.verify = function verify(message) {
if (typeof message !== "object" || message === null)
return "object expected";
return null;
};
/**
* Creates a ReqBetWeakHand message from a plain object. Also converts values to their respective internal types.
* @function fromObject
* @memberof api.ReqBetWeakHand
* @static
* @param {Object.<string,*>} object Plain object
* @returns {api.ReqBetWeakHand} ReqBetWeakHand
*/
ReqBetWeakHand.fromObject = function fromObject(object) {
if (object instanceof $root.api.ReqBetWeakHand)
return object;
return new $root.api.ReqBetWeakHand();
};
/**
* Creates a plain object from a ReqBetWeakHand message. Also converts values to other types if specified.
* @function toObject
* @memberof api.ReqBetWeakHand
* @static
* @param {api.ReqBetWeakHand} message ReqBetWeakHand
* @param {$protobuf.IConversionOptions} [options] Conversion options
* @returns {Object.<string,*>} Plain object
*/
ReqBetWeakHand.toObject = function toObject() {
return {};
};
/**
* Converts this ReqBetWeakHand to JSON.
* @function toJSON
* @memberof api.ReqBetWeakHand
* @instance
* @returns {Object.<string,*>} JSON object
*/
ReqBetWeakHand.prototype.toJSON = function toJSON() {
return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
};
/**
* Gets the default type url for ReqBetWeakHand
* @function getTypeUrl
* @memberof api.ReqBetWeakHand
* @static
* @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
* @returns {string} The default type url
*/
ReqBetWeakHand.getTypeUrl = function getTypeUrl(typeUrlPrefix) {
if (typeUrlPrefix === undefined) {
typeUrlPrefix = "type.googleapis.com";
}
return typeUrlPrefix + "/api.ReqBetWeakHand";
};
return ReqBetWeakHand;
})();
api.ReqBetDiscard = (function() {
/**
* Properties of a ReqBetDiscard.
* @memberof api
* @interface IReqBetDiscard
*/
/**
* Constructs a new ReqBetDiscard.
* @memberof api
* @classdesc Represents a ReqBetDiscard.
* @implements IReqBetDiscard
* @constructor
* @param {api.IReqBetDiscard=} [properties] Properties to set
*/
function ReqBetDiscard(properties) {
if (properties)
for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
if (properties[keys[i]] != null)
this[keys[i]] = properties[keys[i]];
}
/**
* Creates a new ReqBetDiscard instance using the specified properties.
* @function create
* @memberof api.ReqBetDiscard
* @static
* @param {api.IReqBetDiscard=} [properties] Properties to set
* @returns {api.ReqBetDiscard} ReqBetDiscard instance
*/
ReqBetDiscard.create = function create(properties) {
return new ReqBetDiscard(properties);
};
/**
* Encodes the specified ReqBetDiscard message. Does not implicitly {@link api.ReqBetDiscard.verify|verify} messages.
* @function encode
* @memberof api.ReqBetDiscard
* @static
* @param {api.IReqBetDiscard} message ReqBetDiscard message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
ReqBetDiscard.encode = function encode(message, writer) {
if (!writer)
writer = $Writer.create();
return writer;
};
/**
* Encodes the specified ReqBetDiscard message, length delimited. Does not implicitly {@link api.ReqBetDiscard.verify|verify} messages.
* @function encodeDelimited
* @memberof api.ReqBetDiscard
* @static
* @param {api.IReqBetDiscard} message ReqBetDiscard message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
ReqBetDiscard.encodeDelimited = function encodeDelimited(message, writer) {
return this.encode(message, writer).ldelim();
};
/**
* Decodes a ReqBetDiscard message from the specified reader or buffer.
* @function decode
* @memberof api.ReqBetDiscard
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @param {number} [length] Message length if known beforehand
* @returns {api.ReqBetDiscard} ReqBetDiscard
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
ReqBetDiscard.decode = function decode(reader, length) {
if (!(reader instanceof $Reader))
reader = $Reader.create(reader);
let end = length === undefined ? reader.len : reader.pos + length, message = new $root.api.ReqBetDiscard();
while (reader.pos < end) {
let tag = reader.uint32();
switch (tag >>> 3) {
default:
reader.skipType(tag & 7);
break;
}
}
return message;
};
/**
* Decodes a ReqBetDiscard message from the specified reader or buffer, length delimited.
* @function decodeDelimited
* @memberof api.ReqBetDiscard
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @returns {api.ReqBetDiscard} ReqBetDiscard
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
ReqBetDiscard.decodeDelimited = function decodeDelimited(reader) {
if (!(reader instanceof $Reader))
reader = new $Reader(reader);
return this.decode(reader, reader.uint32());
};
/**
* Verifies a ReqBetDiscard message.
* @function verify
* @memberof api.ReqBetDiscard
* @static
* @param {Object.<string,*>} message Plain object to verify
* @returns {string|null} `null` if valid, otherwise the reason why it is not
*/
ReqBetDiscard.verify = function verify(message) {
if (typeof message !== "object" || message === null)
return "object expected";
return null;
};
/**
* Creates a ReqBetDiscard message from a plain object. Also converts values to their respective internal types.
* @function fromObject
* @memberof api.ReqBetDiscard
* @static
* @param {Object.<string,*>} object Plain object
* @returns {api.ReqBetDiscard} ReqBetDiscard
*/
ReqBetDiscard.fromObject = function fromObject(object) {
if (object instanceof $root.api.ReqBetDiscard)
return object;
return new $root.api.ReqBetDiscard();
};
/**
* Creates a plain object from a ReqBetDiscard message. Also converts values to other types if specified.
* @function toObject
* @memberof api.ReqBetDiscard
* @static
* @param {api.ReqBetDiscard} message ReqBetDiscard
* @param {$protobuf.IConversionOptions} [options] Conversion options
* @returns {Object.<string,*>} Plain object
*/
ReqBetDiscard.toObject = function toObject() {
return {};
};
/**
* Converts this ReqBetDiscard to JSON.
* @function toJSON * @function toJSON
* @memberof api.ReqBetDiscard * @memberof api.ReqPlaceBet
* @instance * @instance
* @returns {Object.<string,*>} JSON object * @returns {Object.<string,*>} JSON object
*/ */
ReqBetDiscard.prototype.toJSON = function toJSON() { ReqPlaceBet.prototype.toJSON = function toJSON() {
return this.constructor.toObject(this, $protobuf.util.toJSONOptions); return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
}; };
/** /**
* Gets the default type url for ReqBetDiscard * Gets the default type url for ReqPlaceBet
* @function getTypeUrl * @function getTypeUrl
* @memberof api.ReqBetDiscard * @memberof api.ReqPlaceBet
* @static * @static
* @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com")
* @returns {string} The default type url * @returns {string} The default type url
*/ */
ReqBetDiscard.getTypeUrl = function getTypeUrl(typeUrlPrefix) { ReqPlaceBet.getTypeUrl = function getTypeUrl(typeUrlPrefix) {
if (typeUrlPrefix === undefined) { if (typeUrlPrefix === undefined) {
typeUrlPrefix = "type.googleapis.com"; typeUrlPrefix = "type.googleapis.com";
} }
return typeUrlPrefix + "/api.ReqBetDiscard"; return typeUrlPrefix + "/api.ReqPlaceBet";
}; };
return ReqBetDiscard; return ReqPlaceBet;
})(); })();
api.ResNoticePlayerLine = (function() { api.ResNoticePlayerLine = (function() {

206
internal/game/hand.go

@ -0,0 +1,206 @@
package game
import (
"errors"
"math"
"strconv"
)
// Hand 5张牌的牌型
type Hand struct {
cards [5]*Card // 组合牌型
point int // 组合牌型点数
CardType CardType // 组合牌型名称
}
func (h *Hand) Init(cards [5]*Card) *Hand {
SortCards(&cards)
// 牌型排序降序
face := GetCardFace(cards)
// 计算牌型
switch true {
case isRoyalFlush(cards):
h.CardType = RoyalFlush
case isStraightFlush(cards):
h.CardType = StraightFlush
case isFourOfKind(face):
h.CardType = FourOfKind
case isFullHouse(face):
h.CardType = FullHouse
case isFlush(cards):
h.CardType = Flush
case isStraight(cards):
h.CardType = Straight
case isThreeOfKind(face):
h.CardType = ThreeOfKind
case isTwoPairs(face):
h.CardType = TwoPairs
case isOnePair(face):
h.CardType = OnePair
default:
h.CardType = HighCard
}
h.cards = cards
// 计算牌点数
h.point = int(h.CardType) + getHighCardPoint(cards)
return h
}
type CardType int
func (ct CardType) String() string {
f, _ := strconv.ParseFloat(strconv.Itoa(int(ct)>>13), 32)
return cardTypeNames[int(math.Log2(f))]
}
var cardTypeNames = []string{"HighCard", "OnePair", "TwoPairs", "ThreeOfKind", "Straight", "Flush", "FullHouse", "FourOfKind", "StraightFlush", "RoyalFlush"}
const (
HighCard CardType = 1 << 13 << iota // 高牌
OnePair CardType = 1 << 13 << iota // 对子
TwoPairs CardType = 1 << 13 << iota // 两对
ThreeOfKind CardType = 1 << 13 << iota // 三张
Straight CardType = 1 << 13 << iota // 顺子
Flush CardType = 1 << 13 << iota // 同花
FullHouse CardType = 1 << 13 << iota // 葫芦
FourOfKind CardType = 1 << 13 << iota // 四条
StraightFlush CardType = 1 << 13 << iota // 同花顺
RoyalFlush CardType = 1 << 13 << iota // 皇家同花顺
)
// AnalyzeMaxHand 分析牌型,选出5张最大牌
func AnalyzeMaxHand(handCards [2]*Card, publicCards [5]*Card) (*Hand, error) {
// 计算牌的组合
var allCards = make([]*Card, 0, 7)
for _, hc := range handCards {
if hc != nil {
allCards = append(allCards, hc)
}
}
for _, pc := range publicCards {
if pc != nil {
allCards = append(allCards, pc)
}
}
if len(allCards) < 5 {
return nil, errors.New("all cards count less 5")
}
var cards [5]*Card // 选出的5张牌
// 只能组合一手牌
if len(allCards) == 5 {
copy(cards[:], allCards)
return new(Hand).Init(cards), nil
}
// 查找最大分数组合
// tempCards := allCards[:5]
copy(cards[:], allCards[:5])
maxHand := new(Hand).Init(cards)
for i := 0; i < len(cards); i++ {
for j := 5; j < len(allCards); j++ {
var tempCards = cards
tempCards[i] = allCards[j]
h := new(Hand).Init(tempCards)
if h.point > maxHand.point {
maxHand = h
}
}
}
return maxHand, nil
}
// SortCards 5张牌降序排序
func SortCards(cards *[5]*Card) {
for i := 0; i < len(cards); i++ {
for j := i + 1; j < len(cards); j++ {
if cards[i].Compare(cards[j]) < 0 {
cards[i], cards[j] = cards[j], cards[i]
}
}
}
}
// GetCardFace 分析相同点数的牌个数
// return 0:单牌个数 1:对子个数 2:三条个数 3四条个数
func GetCardFace(cards [5]*Card) [4]int {
var faceMap = make(map[PokerDot]int, 5)
for _, c := range cards {
faceMap[c.Dot]++
}
// 0:单牌个数 1:对子个数 2:三条个数 3四条个数
var face = [4]int{}
for dot := range faceMap {
face[faceMap[dot]-1]++
}
return face
}
// isRoyalFlush 皇家同花顺
func isRoyalFlush(cards [5]*Card) bool {
return PokerA == cards[0].Dot && isStraight(cards) && isFlush(cards)
}
// isStraightFlush 同花顺
func isStraightFlush(cards [5]*Card) bool {
return isStraight(cards) && isFlush(cards)
}
// isFourOfKind 四条
func isFourOfKind(face [4]int) bool {
return face[3] > 0
}
// isFullHouse 葫芦, 3条加2对
func isFullHouse(face [4]int) bool {
return face[2] > 0 && face[1] > 0
}
// isFlush 同花
func isFlush(cards [5]*Card) bool {
firstSuit := cards[0].Suit // 首张牌花色
// 判断花色是否都相同
for i := 1; i < len(cards); i++ {
if firstSuit != cards[i].Suit {
return false
}
}
return true
}
// isStraight 顺子
func isStraight(cards [5]*Card) bool {
var value int64
// 000111110000000
for _, c := range cards {
value |= 1 << int64(c.Dot)
}
// 是顺子 00011111 == 31
return value>>cards[len(cards)-1].Dot == 31
}
// isThreeOfKind 三条
func isThreeOfKind(face [4]int) bool {
return face[2] > 0
}
// isTwoPairs 两对
func isTwoPairs(face [4]int) bool {
return face[1] >= 2
}
// isOnePair 对子
func isOnePair(face [4]int) bool {
return face[1] > 0
}
// getHighCardPoint 高牌,返回散牌点数
func getHighCardPoint(cards [5]*Card) int {
totalPoint := 0
for i, c := range cards {
// 牌权重降序
totalPoint += int(c.Dot) << (4 - i)
}
return totalPoint
}

9
internal/game/player.go

@ -7,9 +7,11 @@ import (
// Player 游戏玩家 // Player 游戏玩家
type Player struct { type Player struct {
Id int64 PosIndex int // 座位索引
Username string Id int64
Avatar string Username string
Avatar string
Cards [2]*Card // 手牌 Cards [2]*Card // 手牌
Chip int32 // 玩家筹码 Chip int32 // 玩家筹码
RoundChip int32 // 该回合已下注筹码 RoundChip int32 // 该回合已下注筹码
@ -19,6 +21,7 @@ type Player struct {
BetMin int32 // 最低下注额 BetMin int32 // 最低下注额
BetMax int32 // 最高下注额 -1不限 BetMax int32 // 最高下注额 -1不限
RoundBetTimes int32 // 当前回合下注次数 RoundBetTimes int32 // 当前回合下注次数
Hand *Hand // 手牌牌型
GameTable *Table // 当前牌桌 GameTable *Table // 当前牌桌
ProtoWriter letter.ProtoWriter ProtoWriter letter.ProtoWriter

139
internal/game/table.go

@ -30,7 +30,8 @@ type Table struct {
TexasType int32 // 德州扑克类型: (1限注德州扑克:你只能增加与大盲注相同的投注额; 2底池限制德州扑克:你只能增加当时台面最大额的投注额(已经完成所有投注); 3无限制德州扑克:你可在手持额度下,增加任何额度的投注额,如果你投入所有筹码,就是“全押”) TexasType int32 // 德州扑克类型: (1限注德州扑克:你只能增加与大盲注相同的投注额; 2底池限制德州扑克:你只能增加当时台面最大额的投注额(已经完成所有投注); 3无限制德州扑克:你可在手持额度下,增加任何额度的投注额,如果你投入所有筹码,就是“全押”)
LimitInAmount int32 // 最低入场金额 LimitInAmount int32 // 最低入场金额
LastPosBetChip int32 // 上家下注金额 LastPosBetChip int32 // 上家下注金额
RoundRaiseTimes int32 // 该轮押注圈加注次数
PlayersLock *sync.Mutex PlayersLock *sync.Mutex
ChipLock *sync.Mutex // 桌面筹码更新锁 ChipLock *sync.Mutex // 桌面筹码更新锁
@ -60,18 +61,34 @@ func (t *Table) PlayerCount() int32 {
} }
func (t *Table) NextPlayerPos(current int) int { func (t *Table) NextPlayerPos(current int) int {
// 当前位置往后
for i := current + 1; i < len(t.Players); i++ { for i := current + 1; i < len(t.Players); i++ {
if t.Players[i] == nil { if t.Players[i] == nil {
continue continue
} }
return i return i
} }
// 从头找
for i, p := range t.Players { for i, p := range t.Players {
if p != nil { if p != nil {
return i return i
} }
} }
return 0 return current
}
// NextHoldingCardPlayerPos 下一个未弃牌玩家
func (t *Table) NextHoldingCardPlayerPos(current int) int {
for i := 0; i < 999; i++ {
nextPos := t.NextPlayerPos(current)
if nextPos == current {
return current
}
if t.Players[nextPos].Status != 7 {
return nextPos
}
}
return current
} }
//func (t *Table) FindPlayerPos(player *Player) int32 { //func (t *Table) FindPlayerPos(player *Player) int32 {
@ -83,17 +100,17 @@ func (t *Table) NextPlayerPos(current int) int {
// return -1 // return -1
//} //}
func (t *Table) JoinPlayer(player *Player) error { func (t *Table) JoinPlayer(player *Player) (int, error) {
t.PlayersLock.Lock() t.PlayersLock.Lock()
defer t.PlayersLock.Unlock() defer t.PlayersLock.Unlock()
for i := range t.Players { for i := range t.Players {
// 找个空位坐下 // 找个空位坐下
if t.Players[i] == nil { if t.Players[i] == nil {
t.Players[i] = player t.Players[i] = player
return nil return i, nil
} }
} }
return errors.New("牌桌玩家已满") return -1, errors.New("牌桌玩家已满")
} }
// BuildResGameFullStatus 构建牌桌当前游戏状态消息 // BuildResGameFullStatus 构建牌桌当前游戏状态消息
@ -176,6 +193,118 @@ func (t *Table) NoticeAllPlayer(message proto.Message) {
} }
} }
// CurrentRoundEnd 当前回合结束
func (t *Table) CurrentRoundEnd() error {
// TODO 游戏结束,开牌结算
if t.RoundTimes >= 4 {
// 比牌
t.Stage = 7
var winners []*Player = nil
for _, p := range t.Players {
if p != nil {
continue
}
hand, err := AnalyzeMaxHand(p.Cards, t.PublicCards)
if err != nil {
return err
}
p.Hand = hand
if winners == nil {
winners = []*Player{p}
} else {
if p.Hand.point > winners[0].Hand.point {
winners[0] = p
} else if p.Hand.point == winners[0].Hand.point {
// 手牌相同
winners = append(winners, p)
}
}
}
winnerCount := int32(len(winners))
// TODO 所有玩家输赢消息 ResCalcWinnerChip
if winnerCount == 1 {
// 只有一个玩家获胜
winner := winners[0]
winner.Chip += t.Chip
t.Chip = 0
} else {
// 平分奖池
once := t.Chip / winnerCount
lessPos := winners[0].PosIndex
lessNear := lessPos - t.SmallBlindPos
for _, p := range winners {
p.Chip += once
// 计算最靠近发牌者的赢家
pNear := p.PosIndex - t.SmallBlindPos
if pNear*lessPos < 0 { // 一正一负, 正的近
if pNear >= 0 {
lessPos = p.PosIndex
}
} else if lessNear > pNear {
lessPos = p.PosIndex
}
}
// 无法平分之小额筹码,顺时针靠近发牌者得
less := t.Chip - once*winnerCount
if less > 0 {
t.Players[lessPos].Chip += less
}
t.Chip = 0
}
go func() {
// TODO 等前端展示3秒钟后,牌桌回到初始状态,剔除掉线玩家
}()
return nil
}
// 开启下一回合, 重置一些状态
t.RoundRaiseTimes = 0
t.RoundTimes++
for _, p := range t.Players {
if p != nil {
p.RoundBetTimes = 0
p.RoundChip = 0
p.BetMin = 0
p.BetMax = 0
p.BetOpts = []int32{}
p.SetStatus(3)
}
}
// 由小盲注或小盲注后第一个未弃牌玩家开始
nextBegin := t.SmallBlindPos
if t.Players[t.SmallBlindPos].Status == 7 {
nextBegin = t.NextHoldingCardPlayerPos(nextBegin)
}
begin := t.Players[nextBegin]
begin.SetStatus(6)
begin.BetOpts = []int32{1, 2, 4, 5}
// 根据回合发公共牌
switch t.RoundTimes {
case 2: // 发3张公共牌(翻牌)
for i := 0; i < 5; i++ {
if i < 3 {
t.PublicCards[i] = t.Dealer.Deal()
continue
}
// 第4,5张牌置空
t.PublicCards[i] = nil
}
case 3: // 发第4张公共牌(转牌turn)
t.PublicCards[3] = t.Dealer.Deal()
t.Stage = 5
case 4: // 发第5张公共牌(河牌river)
t.PublicCards[4] = t.Dealer.Deal()
t.Stage = 6
}
return nil
}
// DiscardAllPlayerWin 其他玩家弃牌,玩家赢
func (t *Table) DiscardAllPlayerWin(player *Player) {
}
// NoticeGamePlayerCall TODO 发送(下注信息、下注人、下一位下注人)到牌桌所有用户 // NoticeGamePlayerCall TODO 发送(下注信息、下注人、下一位下注人)到牌桌所有用户
//func (t *Table) NoticeGamePlayerCall() { //func (t *Table) NoticeGamePlayerCall() {
// //

2
internal/service/auth.go

@ -186,7 +186,7 @@ func HandleReqIdentity(client *session.NetClient, msg *api.ReqIdentity) (proto.M
UserName: subject.Name, UserName: subject.Name,
Avatar: subject.Avatar, Avatar: subject.Avatar,
Client: client, Client: client,
Balance: 0, Balance: 1000,
BalanceLock: &sync.RWMutex{}, BalanceLock: &sync.RWMutex{},
Lock: &sync.Mutex{}, Lock: &sync.Mutex{},
} }

191
internal/service/table.go

@ -68,6 +68,7 @@ func HandleReqCreateTable(account *session.NetAccount, msg *api.ReqCreateTable)
// 挂载 player 到 account 上 // 挂载 player 到 account 上
account.Player = owner account.Player = owner
table.Players[0] = owner table.Players[0] = owner
owner.PosIndex = 0
for i := 0; i < int(robotNum); i++ { for i := 0; i < int(robotNum); i++ {
table.Robots[i] = &game.Robot{} table.Robots[i] = &game.Robot{}
@ -158,10 +159,12 @@ func HandleReqJoinTable(account *session.NetAccount, msg *api.ReqJoinTable) (pro
ProtoWriter: account.Client, ProtoWriter: account.Client,
} }
player.Init() player.Init()
err := table.JoinPlayer(player) index, err := table.JoinPlayer(player)
if err != nil { if err != nil {
return nil, err return nil, err
} }
player.PosIndex = index
// 从账户扣除进入牌桌的金额 // 从账户扣除进入牌桌的金额
account.DecrementBalance(table.LimitInAmount) account.DecrementBalance(table.LimitInAmount)
player.Chip = table.LimitInAmount player.Chip = table.LimitInAmount
@ -393,15 +396,6 @@ func HandleReqGameStart(player *game.Player, msg *api.ReqGameStart) (proto.Messa
// 玩家状态置为等待 // 玩家状态置为等待
p.SetStatus(3) p.SetStatus(3)
} }
// 发3张公共牌
//for i := 0; i < 5; i++ {
// if i < 3 {
// table.PublicCards[i] = table.Dealer.Deal()
// continue
// }
// // 第4,5张牌置空
// table.PublicCards[i] = nil
//}
// 待大盲注位下一位玩家下注 // 待大盲注位下一位玩家下注
optPos := table.NextPlayerPos(table.BigBlindPos) optPos := table.NextPlayerPos(table.BigBlindPos)
@ -453,7 +447,7 @@ func HandleReqGameStart(player *game.Player, msg *api.ReqGameStart) (proto.Messa
return &api.ResSuccess{}, nil return &api.ResSuccess{}, nil
} }
// HandleReqPlaceBetChip 下注 // HandleReqPlaceBet 下注
// 网易德州扑克:http://sports.163.com/special/poker_rule/?ivk_sa=1025883k // 网易德州扑克:http://sports.163.com/special/poker_rule/?ivk_sa=1025883k
// 天天德州: http://game.people.com.cn/n/2014/0220/c40130-24413078.html // 天天德州: http://game.people.com.cn/n/2014/0220/c40130-24413078.html
// wiki: https://zh.wikipedia.org/wiki/%E5%BE%B7%E5%B7%9E%E6%92%B2%E5%85%8B // wiki: https://zh.wikipedia.org/wiki/%E5%BE%B7%E5%B7%9E%E6%92%B2%E5%85%8B
@ -461,7 +455,11 @@ func HandleReqGameStart(player *game.Player, msg *api.ReqGameStart) (proto.Messa
// 德州扑克多名玩家ALL IN之后的筹码 如何分配?https://www.zhihu.com/question/32187371 // 德州扑克多名玩家ALL IN之后的筹码 如何分配?https://www.zhihu.com/question/32187371
// All-in技巧:https://bac88.net/%E5%BE%B7%E5%B7%9E%E6%92%B2%E5%85%8B%E6%8A%80%E5%B7%A7-all-in%E7%9A%84%E6%8A%80%E5%B7%A7/ // All-in技巧:https://bac88.net/%E5%BE%B7%E5%B7%9E%E6%92%B2%E5%85%8B%E6%8A%80%E5%B7%A7-all-in%E7%9A%84%E6%8A%80%E5%B7%A7/
// 起手牌组合概率:https://www.moshike.com/a/1011.html // 起手牌组合概率:https://www.moshike.com/a/1011.html
func HandleReqPlaceBetChip(player *game.Player, msg *api.ReqPlaceBetChip) (proto.Message, error) { // 各圈喊注将持续直至每个牌手都已为以下三种状态之一:
// 盖牌(fold),即舍弃并覆盖手中的牌,放弃已投入底池的筹码退出该局。
// 跟注(call)投入了与所有其他未盖牌的牌手等量的筹码。
// 全下(all-in),投入了尚余的全部筹码。
func HandleReqPlaceBet(player *game.Player, msg *api.ReqPlaceBet) (proto.Message, error) {
player.Lock.Lock() player.Lock.Lock()
defer player.Lock.Unlock() defer player.Lock.Unlock()
player.GameTable.ChipLock.Lock() player.GameTable.ChipLock.Lock()
@ -470,62 +468,143 @@ func HandleReqPlaceBetChip(player *game.Player, msg *api.ReqPlaceBetChip) (proto
if !collect.In(player.Status, 4, 6) { if !collect.In(player.Status, 4, 6) {
return &api.ResFail{Msg: "当前未轮到您下注"}, nil return &api.ResFail{Msg: "当前未轮到您下注"}, nil
} }
switch player.Status { if !collect.In(msg.BetType, player.BetOpts...) {
case 4: // 小盲注下注,下注金额最小大盲注1半, return &api.ResFail{Msg: fmt.Sprintf("当前不可执行该操作#%d", msg.BetType)}, nil
if msg.BetChip < player.GameTable.BigBlindChip/2 { }
return &api.ResFail{Msg: fmt.Sprintf("您当前最小下注金额%d", player.GameTable.BigBlindChip/2)}, nil
table := player.GameTable
var line1 string
var line2 string
var raise bool // 是否加注
switch msg.BetType {
case 1: // 跟注
if msg.BetChip != player.BetMin {
return &api.ResFail{Msg: fmt.Sprintf("投注大于应跟注金额#%d", player.BetMin)}, nil
} }
case 6: // 轮流下注,下注金额最小大盲注 fallthrough
if msg.BetChip < player.GameTable.BigBlindChip { case 2: // 加注
return &api.ResFail{Msg: fmt.Sprintf("您当前最小下注金额%d", player.GameTable.BigBlindChip)}, nil if table.LastPosBetChip < 0 {
return &api.ResFail{Msg: "您当前只能All-In或弃牌"}, nil
} }
default: if msg.BetChip < player.BetMin {
return &api.ResFail{Msg: "当前未轮到您下注"}, nil return &api.ResFail{Msg: fmt.Sprintf("小于当前最低投注额#%d", player.BetMin)}, nil
} }
// 检查最大下注金额 if player.BetMax > 0 && msg.BetChip > player.BetMax {
var maxBetChip int32 = 0 return &api.ResFail{Msg: fmt.Sprintf("大于当前最大投注额#%d", player.BetMax)}, nil
// 依赌场规则而异, }
// 若所余筹码All-in后仍低于最低加注金额,则此注仅视为跟注(call)而不能被当成加注(raise),亦及该圈若未有其他人再加注,则原加注者(此例中的第一位牌手)仅可跟注补齐或盖牌,于此圈不可再行加注 // 限注式加注3次判断
// 亦有少部分赌场规定All-in后大于最低加注额的一半以上,原加注者即可重新加注。 if table.TexasType == 1 && msg.BetChip > player.BetMin && table.RoundRaiseTimes >= 3 {
return &api.ResFail{Msg: "该轮不可再加注"}, nil
}
// TODO 加注判断
if msg.BetChip > table.LastPosBetChip {
table.RoundRaiseTimes++
}
player.RoundBetTimes++
raise = msg.BetChip > player.BetMin
table.LastPosBetChip = msg.BetChip
table.Chip += msg.BetChip
player.Chip -= msg.BetChip
case 3: // All-In TODO 边池
player.RoundBetTimes++
table.LastPosBetChip = player.Chip
table.Chip += player.Chip
player.Chip = 0
table.LastPosBetChip = msg.BetChip
case 4: // 弃牌
player.SetStatus(7)
line1 = "弃牌"
// 剩余牌手数为1则结束
leftPlayers := collect.Filter(player.GameTable.Players, func(i int, p *game.Player) bool {
return p != nil && p.Status != 7
})
if len(leftPlayers) == 1 {
playerWin(leftPlayers[0])
return &api.ResSuccess{}, nil
}
case 5: // 过牌
player.RoundBetTimes++
line1 = "过牌"
allIn := false
fmt.Println(allIn)
switch player.GameTable.TexasType {
case 1: // 大盲注
maxBetChip = player.GameTable.BigBlindChip
case 2: // 台面最大额
maxBetChip = player.GameTable.Chip
case 3: // 最大All In
maxBetChip = player.Chip
default: default:
return &api.ResFail{Msg: "牌桌"}, nil return &api.ResFail{Msg: fmt.Sprintf("未知操作#%d", msg.BetType)}, nil
} }
if player.GameTable.TexasType == 3 { // 通知下注结果
// 无限制可All In if line1 != "" {
allIn = msg.BetChip == maxBetChip notice := &api.ResNoticePlayerLine{PlayerId: player.Id, Line1: line1, Line2: line2}
player.GameTable.NoticeAllPlayer(notice)
} else if player.Status == 4 {
// 最大金额减去小盲注金额
maxBetChip -= player.GameTable.BigBlindChip / 2
}
if msg.BetChip > maxBetChip {
return &api.ResFail{Msg: fmt.Sprintf("您当前最大下注金额%d", maxBetChip)}, nil
} }
player.Chip -= msg.BetChip player.SetStatus(3)
return nil, nil // 下注完成:若还有玩家未下注、加注,下一玩家继续下注;若该轮下注完成,发牌开启下一轮下注
} // 下一位玩家:跟注
var nextPlayer *game.Player = player
for {
nextPlayerPos := table.NextPlayerPos(nextPlayer.PosIndex)
nextPlayer = table.Players[nextPlayerPos]
if nextPlayer.Id == player.Id {
// TODO 开启下一轮
break
}
if nextPlayer.Status == 7 { // 已弃牌..
continue
}
if !raise {
// 当前玩家未加注, 判断是否结束本轮下注
if nextPlayer.RoundBetTimes > 0 {
// TODO 开启下一轮
}
}
// 判断下一玩家是否需要再下注: 下一玩家是否已下注,当前玩家是否跟注,
nextPlayer.SetStatus(6)
nextPlayer.BetMin = msg.BetChip
nextPlayer.BetOpts = []int32{1, 4}
// 下一玩家需跟注
// 如果当前玩家是小盲注第一次下注
if player.RoundBetTimes == 1 && table.SmallBlindPos == player.PosIndex {
nextPlayer.BetMin += table.SmallBlindChip
}
// 如果下一玩家是大盲注第一次下注
if nextPlayer.RoundBetTimes == 0 && table.BigBlindPos == nextPlayerPos {
nextPlayer.BetMin -= table.BigBlindChip
}
// 游戏类型处理最大下注额
switch table.TexasType {
case 2: // 底池限注
nextPlayer.BetOpts = append(nextPlayer.BetOpts, 3)
nextPlayer.BetMax = table.Chip + table.LastPosBetChip*2
case 3: // 无限注
nextPlayer.BetOpts = append(nextPlayer.BetOpts, 2, 3) // 可 all in
nextPlayer.BetMax = -1
case 1: // 限注
fallthrough
default:
// 限注比赛只允许一次下注与三次加注
if table.RoundRaiseTimes < 3 {
nextPlayer.BetOpts = append(nextPlayer.BetOpts, 3)
// 第1,2回合跟注加注需和大盲注相同, 3,4回合两倍
nextPlayer.BetMax = table.BigBlindChip * ((table.RoundTimes+1)/2 + 1)
} else {
// 只能跟注
nextPlayer.BetMax = msg.BetChip
}
}
}
// HandleReqBetWeakHand 让牌 // 依赌场规则而异,
func HandleReqBetWeakHand(player *game.Player, msg *api.ReqBetWeakHand) (proto.Message, error) { // 若所余筹码All-in后仍低于最低加注金额,则此注仅视为跟注(call)而不能被当成加注(raise),亦及该圈若未有其他人再加注,则原加注者(此例中的第一位牌手)仅可跟注补齐或盖牌,于此圈不可再行加注
// 亦有少部分赌场规定All-in后大于最低加注额的一半以上,原加注者即可重新加注。
return nil, nil return &api.ResSuccess{}, nil
} }
// HandleReqBetDiscard 弃牌 // playerWin 玩家获胜,结算牌局
func HandleReqBetDiscard(player *game.Player, msg *api.ReqBetDiscard) (proto.Message, error) { func playerWin(player *game.Player) {
return nil, nil
} }

20
tool/collect/collect.go

@ -8,3 +8,23 @@ func In[T comparable](value T, values ...T) bool {
} }
return false return false
} }
func Count[T any](slice []T, isCount func(int, T) bool) int {
count := 0
for i, item := range slice {
if isCount(i, item) {
count++
}
}
return count
}
func Filter[T any](slice []T, predicate func(int, T) bool) []T {
res := []T{}
for i, item := range slice {
if predicate(i, item) {
res = append(res, item)
}
}
return res
}

Loading…
Cancel
Save