diff --git a/api/operation.go b/api/operation.go index f7498c7..eef8c1d 100644 --- a/api/operation.go +++ b/api/operation.go @@ -47,9 +47,7 @@ var protoInstances = []proto.Message{ &ResDismissGameTable{}, &ReqGameStart{}, - &ReqPlaceBetChip{}, - &ReqBetWeakHand{}, - &ReqBetDiscard{}, + &ReqPlaceBet{}, &ResNoticePlayerLine{}, } diff --git a/api/poker.pb.go b/api/poker.pb.go index cc2c209..657d036 100644 --- a/api/poker.pb.go +++ b/api/poker.pb.go @@ -1607,17 +1607,17 @@ func (*ResDismissGameTable) Descriptor() ([]byte, []int) { } // 下注 -type ReqPlaceBetChip struct { +type ReqPlaceBet struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - BetChip int32 `protobuf:"varint,1,opt,name=betChip,proto3" json:"betChip,omitempty"` // 跟注/加注金额 - AllIn bool `protobuf:"varint,2,opt,name=allIn,proto3" json:"allIn,omitempty"` // 是否 allIn + BetType int32 `protobuf:"varint,1,opt,name=betType,proto3" json:"betType,omitempty"` // 1跟注,2加注(-[0]+),3All-In,4弃牌,5过牌 + BetChip int32 `protobuf:"varint,2,opt,name=betChip,proto3" json:"betChip,omitempty"` // 跟注/加注金额 } -func (x *ReqPlaceBetChip) Reset() { - *x = ReqPlaceBetChip{} +func (x *ReqPlaceBet) Reset() { + *x = ReqPlaceBet{} if protoimpl.UnsafeEnabled { mi := &file_poker_proto_msgTypes[29] 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) } -func (*ReqPlaceBetChip) ProtoMessage() {} +func (*ReqPlaceBet) ProtoMessage() {} -func (x *ReqPlaceBetChip) ProtoReflect() protoreflect.Message { +func (x *ReqPlaceBet) ProtoReflect() protoreflect.Message { mi := &file_poker_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1643,101 +1643,23 @@ func (x *ReqPlaceBetChip) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ReqPlaceBetChip.ProtoReflect.Descriptor instead. -func (*ReqPlaceBetChip) Descriptor() ([]byte, []int) { +// Deprecated: Use ReqPlaceBet.ProtoReflect.Descriptor instead. +func (*ReqPlaceBet) Descriptor() ([]byte, []int) { return file_poker_proto_rawDescGZIP(), []int{29} } -func (x *ReqPlaceBetChip) GetBetChip() int32 { +func (x *ReqPlaceBet) GetBetType() int32 { if x != nil { - return x.BetChip + return x.BetType } return 0 } -func (x *ReqPlaceBetChip) GetAllIn() bool { +func (x *ReqPlaceBet) GetBetChip() int32 { if x != nil { - return x.AllIn - } - 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 x.BetChip } - return mi.MessageOf(x) -} - -// Deprecated: Use ReqBetDiscard.ProtoReflect.Descriptor instead. -func (*ReqBetDiscard) Descriptor() ([]byte, []int) { - return file_poker_proto_rawDescGZIP(), []int{31} + return 0 } // 通知玩家某个其他玩家动作 @@ -1755,7 +1677,7 @@ type ResNoticePlayerLine struct { func (x *ResNoticePlayerLine) Reset() { *x = ResNoticePlayerLine{} if protoimpl.UnsafeEnabled { - mi := &file_poker_proto_msgTypes[32] + mi := &file_poker_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1768,7 +1690,7 @@ func (x *ResNoticePlayerLine) String() string { func (*ResNoticePlayerLine) ProtoMessage() {} func (x *ResNoticePlayerLine) ProtoReflect() protoreflect.Message { - mi := &file_poker_proto_msgTypes[32] + mi := &file_poker_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1781,7 +1703,7 @@ func (x *ResNoticePlayerLine) ProtoReflect() protoreflect.Message { // Deprecated: Use ResNoticePlayerLine.ProtoReflect.Descriptor instead. func (*ResNoticePlayerLine) Descriptor() ([]byte, []int) { - return file_poker_proto_rawDescGZIP(), []int{32} + return file_poker_proto_rawDescGZIP(), []int{30} } 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, 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, - 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x41, 0x0a, 0x0f, 0x52, 0x65, 0x71, 0x50, 0x6c, - 0x61, 0x63, 0x65, 0x42, 0x65, 0x74, 0x43, 0x68, 0x69, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x65, - 0x74, 0x43, 0x68, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x62, 0x65, 0x74, - 0x43, 0x68, 0x69, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x6c, 0x49, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x6c, 0x49, 0x6e, 0x22, 0x10, 0x0a, 0x0e, 0x52, 0x65, - 0x71, 0x42, 0x65, 0x74, 0x57, 0x65, 0x61, 0x6b, 0x48, 0x61, 0x6e, 0x64, 0x22, 0x0f, 0x0a, 0x0d, - 0x52, 0x65, 0x71, 0x42, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x22, 0x73, 0x0a, - 0x13, 0x52, 0x65, 0x73, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, - 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x31, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x18, - 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x12, 0x14, 0x0a, 0x05, - 0x6c, 0x69, 0x6e, 0x65, 0x33, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, - 0x65, 0x33, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x61, 0x70, 0x69, 0x3b, 0x61, 0x70, 0x69, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x41, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x50, 0x6c, + 0x61, 0x63, 0x65, 0x42, 0x65, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x65, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x62, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x62, 0x65, 0x74, 0x43, 0x68, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x07, 0x62, 0x65, 0x74, 0x43, 0x68, 0x69, 0x70, 0x22, 0x73, 0x0a, 0x13, 0x52, 0x65, + 0x73, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4c, 0x69, 0x6e, + 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, + 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, + 0x6e, 0x65, 0x31, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, + 0x65, 0x33, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x33, 0x42, + 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x61, 0x70, 0x69, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1966,7 +1886,7 @@ func file_poker_proto_rawDescGZIP() []byte { 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{}{ (*ProtoWrap)(nil), // 0: api.ProtoWrap (*Ping)(nil), // 1: api.Ping @@ -1997,10 +1917,8 @@ var file_poker_proto_goTypes = []interface{}{ (*ReqGameStart)(nil), // 26: api.ReqGameStart (*ReqDismissGameTable)(nil), // 27: api.ReqDismissGameTable (*ResDismissGameTable)(nil), // 28: api.ResDismissGameTable - (*ReqPlaceBetChip)(nil), // 29: api.ReqPlaceBetChip - (*ReqBetWeakHand)(nil), // 30: api.ReqBetWeakHand - (*ReqBetDiscard)(nil), // 31: api.ReqBetDiscard - (*ResNoticePlayerLine)(nil), // 32: api.ResNoticePlayerLine + (*ReqPlaceBet)(nil), // 29: api.ReqPlaceBet + (*ResNoticePlayerLine)(nil), // 30: api.ResNoticePlayerLine } var file_poker_proto_depIdxs = []int32{ 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{} { - switch v := v.(*ReqPlaceBetChip); i { + switch v := v.(*ReqPlaceBet); i { case 0: return &v.state case 1: @@ -2383,30 +2301,6 @@ func file_poker_proto_init() { } } 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 { case 0: return &v.state @@ -2425,7 +2319,7 @@ func file_poker_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_poker_proto_rawDesc, NumEnums: 0, - NumMessages: 33, + NumMessages: 31, NumExtensions: 0, NumServices: 0, }, diff --git a/api/poker.proto b/api/poker.proto index e061e87..8ab5ce4 100644 --- a/api/poker.proto +++ b/api/poker.proto @@ -178,20 +178,9 @@ message ResDismissGameTable { } // 下注 -message ReqPlaceBetChip { - int32 betChip = 1; // 跟注/加注金额 - bool allIn = 2; // 是否 allIn - -} - -// 让牌 -message ReqBetWeakHand { - -} - -// 弃牌 -message ReqBetDiscard { - +message ReqPlaceBet { + int32 betType = 1; // 1跟注,2加注(-[0]+),3All-In,4弃牌,5过牌 + int32 betChip = 2; // 跟注/加注金额 } // 通知玩家某个其他玩家动作 @@ -202,3 +191,8 @@ message ResNoticePlayerLine { string line2 = 12; string line3 = 13; } + +// 玩家输赢筹码通知 +message ResCalcWinnerChip { + map playersWinChip = 9; +} diff --git a/api/proto.js b/api/proto.js index 6a21602..293cce1 100644 --- a/api/proto.js +++ b/api/proto.js @@ -6706,25 +6706,25 @@ export const api = $root.api = (() => { return ResDismissGameTable; })(); - api.ReqPlaceBetChip = (function() { + api.ReqPlaceBet = (function() { /** - * Properties of a ReqPlaceBetChip. + * Properties of a ReqPlaceBet. * @memberof api - * @interface IReqPlaceBetChip - * @property {number|null} [betChip] ReqPlaceBetChip betChip - * @property {boolean|null} [allIn] ReqPlaceBetChip allIn + * @interface IReqPlaceBet + * @property {number|null} [betType] ReqPlaceBet betType + * @property {number|null} [betChip] ReqPlaceBet betChip */ /** - * Constructs a new ReqPlaceBetChip. + * Constructs a new ReqPlaceBet. * @memberof api - * @classdesc Represents a ReqPlaceBetChip. - * @implements IReqPlaceBetChip + * @classdesc Represents a ReqPlaceBet. + * @implements IReqPlaceBet * @constructor - * @param {api.IReqPlaceBetChip=} [properties] Properties to set + * @param {api.IReqPlaceBet=} [properties] Properties to set */ - function ReqPlaceBetChip(properties) { + function ReqPlaceBet(properties) { if (properties) for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) if (properties[keys[i]] != null) @@ -6732,89 +6732,89 @@ export const api = $root.api = (() => { } /** - * ReqPlaceBetChip betChip. - * @member {number} betChip - * @memberof api.ReqPlaceBetChip + * ReqPlaceBet betType. + * @member {number} betType + * @memberof api.ReqPlaceBet * @instance */ - ReqPlaceBetChip.prototype.betChip = 0; + ReqPlaceBet.prototype.betType = 0; /** - * ReqPlaceBetChip allIn. - * @member {boolean} allIn - * @memberof api.ReqPlaceBetChip + * ReqPlaceBet betChip. + * @member {number} betChip + * @memberof api.ReqPlaceBet * @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 - * @memberof api.ReqPlaceBetChip + * @memberof api.ReqPlaceBet * @static - * @param {api.IReqPlaceBetChip=} [properties] Properties to set - * @returns {api.ReqPlaceBetChip} ReqPlaceBetChip instance + * @param {api.IReqPlaceBet=} [properties] Properties to set + * @returns {api.ReqPlaceBet} ReqPlaceBet instance */ - ReqPlaceBetChip.create = function create(properties) { - return new ReqPlaceBetChip(properties); + ReqPlaceBet.create = function create(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 - * @memberof api.ReqPlaceBetChip + * @memberof api.ReqPlaceBet * @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 * @returns {$protobuf.Writer} Writer */ - ReqPlaceBetChip.encode = function encode(message, writer) { + ReqPlaceBet.encode = function encode(message, writer) { if (!writer) 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")) - writer.uint32(/* id 1, wireType 0 =*/8).int32(message.betChip); - if (message.allIn != null && Object.hasOwnProperty.call(message, "allIn")) - writer.uint32(/* id 2, wireType 0 =*/16).bool(message.allIn); + writer.uint32(/* id 2, wireType 0 =*/16).int32(message.betChip); 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 - * @memberof api.ReqPlaceBetChip + * @memberof api.ReqPlaceBet * @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 * @returns {$protobuf.Writer} Writer */ - ReqPlaceBetChip.encodeDelimited = function encodeDelimited(message, writer) { + ReqPlaceBet.encodeDelimited = function encodeDelimited(message, writer) { 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 - * @memberof api.ReqPlaceBetChip + * @memberof api.ReqPlaceBet * @static * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from * @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 {$protobuf.util.ProtocolError} If required fields are missing */ - ReqPlaceBetChip.decode = function decode(reader, length) { + ReqPlaceBet.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.ReqPlaceBetChip(); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.api.ReqPlaceBet(); while (reader.pos < end) { let tag = reader.uint32(); switch (tag >>> 3) { case 1: { - message.betChip = reader.int32(); + message.betType = reader.int32(); break; } case 2: { - message.allIn = reader.bool(); + message.betChip = reader.int32(); break; } 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 - * @memberof api.ReqPlaceBetChip + * @memberof api.ReqPlaceBet * @static * @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 {$protobuf.util.ProtocolError} If required fields are missing */ - ReqPlaceBetChip.decodeDelimited = function decodeDelimited(reader) { + ReqPlaceBet.decodeDelimited = function decodeDelimited(reader) { if (!(reader instanceof $Reader)) reader = new $Reader(reader); return this.decode(reader, reader.uint32()); }; /** - * Verifies a ReqPlaceBetChip message. + * Verifies a ReqPlaceBet message. * @function verify - * @memberof api.ReqPlaceBetChip + * @memberof api.ReqPlaceBet * @static * @param {Object.} message Plain object to verify * @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) 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 (!$util.isInteger(message.betChip)) return "betChip: integer expected"; - if (message.allIn != null && message.hasOwnProperty("allIn")) - if (typeof message.allIn !== "boolean") - return "allIn: boolean expected"; 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 - * @memberof api.ReqPlaceBetChip + * @memberof api.ReqPlaceBet * @static * @param {Object.} object Plain object - * @returns {api.ReqPlaceBetChip} ReqPlaceBetChip + * @returns {api.ReqPlaceBet} ReqPlaceBet */ - ReqPlaceBetChip.fromObject = function fromObject(object) { - if (object instanceof $root.api.ReqPlaceBetChip) + ReqPlaceBet.fromObject = function fromObject(object) { + if (object instanceof $root.api.ReqPlaceBet) 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) message.betChip = object.betChip | 0; - if (object.allIn != null) - message.allIn = Boolean(object.allIn); 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 - * @memberof api.ReqPlaceBetChip + * @memberof api.ReqPlaceBet * @static - * @param {api.ReqPlaceBetChip} message ReqPlaceBetChip + * @param {api.ReqPlaceBet} message ReqPlaceBet * @param {$protobuf.IConversionOptions} [options] Conversion options * @returns {Object.} Plain object */ - ReqPlaceBetChip.toObject = function toObject(message, options) { + ReqPlaceBet.toObject = function toObject(message, options) { if (!options) options = {}; let object = {}; if (options.defaults) { + object.betType = 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")) object.betChip = message.betChip; - if (message.allIn != null && message.hasOwnProperty("allIn")) - object.allIn = message.allIn; return object; }; /** - * Converts this ReqPlaceBetChip to JSON. - * @function toJSON - * @memberof api.ReqPlaceBetChip - * @instance - * @returns {Object.} 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.} 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.} 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.} Plain object - */ - ReqBetWeakHand.toObject = function toObject() { - return {}; - }; - - /** - * Converts this ReqBetWeakHand to JSON. - * @function toJSON - * @memberof api.ReqBetWeakHand - * @instance - * @returns {Object.} 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.} 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.} 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.} Plain object - */ - ReqBetDiscard.toObject = function toObject() { - return {}; - }; - - /** - * Converts this ReqBetDiscard to JSON. + * Converts this ReqPlaceBet to JSON. * @function toJSON - * @memberof api.ReqBetDiscard + * @memberof api.ReqPlaceBet * @instance * @returns {Object.} JSON object */ - ReqBetDiscard.prototype.toJSON = function toJSON() { + ReqPlaceBet.prototype.toJSON = function toJSON() { return this.constructor.toObject(this, $protobuf.util.toJSONOptions); }; /** - * Gets the default type url for ReqBetDiscard + * Gets the default type url for ReqPlaceBet * @function getTypeUrl - * @memberof api.ReqBetDiscard + * @memberof api.ReqPlaceBet * @static * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") * @returns {string} The default type url */ - ReqBetDiscard.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + ReqPlaceBet.getTypeUrl = function getTypeUrl(typeUrlPrefix) { if (typeUrlPrefix === undefined) { typeUrlPrefix = "type.googleapis.com"; } - return typeUrlPrefix + "/api.ReqBetDiscard"; + return typeUrlPrefix + "/api.ReqPlaceBet"; }; - return ReqBetDiscard; + return ReqPlaceBet; })(); api.ResNoticePlayerLine = (function() { diff --git a/internal/game/hand.go b/internal/game/hand.go new file mode 100644 index 0000000..8d90086 --- /dev/null +++ b/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 +} diff --git a/internal/game/player.go b/internal/game/player.go index 4a76d09..d9eaa50 100644 --- a/internal/game/player.go +++ b/internal/game/player.go @@ -7,9 +7,11 @@ import ( // Player 游戏玩家 type Player struct { - Id int64 - Username string - Avatar string + PosIndex int // 座位索引 + Id int64 + Username string + Avatar string + Cards [2]*Card // 手牌 Chip int32 // 玩家筹码 RoundChip int32 // 该回合已下注筹码 @@ -19,6 +21,7 @@ type Player struct { BetMin int32 // 最低下注额 BetMax int32 // 最高下注额 -1不限 RoundBetTimes int32 // 当前回合下注次数 + Hand *Hand // 手牌牌型 GameTable *Table // 当前牌桌 ProtoWriter letter.ProtoWriter diff --git a/internal/game/table.go b/internal/game/table.go index 527f1f1..ba35641 100644 --- a/internal/game/table.go +++ b/internal/game/table.go @@ -30,7 +30,8 @@ type Table struct { TexasType int32 // 德州扑克类型: (1限注德州扑克:你只能增加与大盲注相同的投注额; 2底池限制德州扑克:你只能增加当时台面最大额的投注额(已经完成所有投注); 3无限制德州扑克:你可在手持额度下,增加任何额度的投注额,如果你投入所有筹码,就是“全押”) LimitInAmount int32 // 最低入场金额 - LastPosBetChip int32 // 上家下注金额 + LastPosBetChip int32 // 上家下注金额 + RoundRaiseTimes int32 // 该轮押注圈加注次数 PlayersLock *sync.Mutex ChipLock *sync.Mutex // 桌面筹码更新锁 @@ -60,18 +61,34 @@ func (t *Table) PlayerCount() int32 { } func (t *Table) NextPlayerPos(current int) int { + // 当前位置往后 for i := current + 1; i < len(t.Players); i++ { if t.Players[i] == nil { continue } return i } + // 从头找 for i, p := range t.Players { if p != nil { 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 { @@ -83,17 +100,17 @@ func (t *Table) NextPlayerPos(current int) int { // return -1 //} -func (t *Table) JoinPlayer(player *Player) error { +func (t *Table) JoinPlayer(player *Player) (int, error) { t.PlayersLock.Lock() defer t.PlayersLock.Unlock() for i := range t.Players { // 找个空位坐下 if t.Players[i] == nil { t.Players[i] = player - return nil + return i, nil } } - return errors.New("牌桌玩家已满") + return -1, errors.New("牌桌玩家已满") } // 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 发送(下注信息、下注人、下一位下注人)到牌桌所有用户 //func (t *Table) NoticeGamePlayerCall() { // diff --git a/internal/service/auth.go b/internal/service/auth.go index 1a00804..a0c29cc 100644 --- a/internal/service/auth.go +++ b/internal/service/auth.go @@ -186,7 +186,7 @@ func HandleReqIdentity(client *session.NetClient, msg *api.ReqIdentity) (proto.M UserName: subject.Name, Avatar: subject.Avatar, Client: client, - Balance: 0, + Balance: 1000, BalanceLock: &sync.RWMutex{}, Lock: &sync.Mutex{}, } diff --git a/internal/service/table.go b/internal/service/table.go index 6f0994a..96430c3 100644 --- a/internal/service/table.go +++ b/internal/service/table.go @@ -68,6 +68,7 @@ func HandleReqCreateTable(account *session.NetAccount, msg *api.ReqCreateTable) // 挂载 player 到 account 上 account.Player = owner table.Players[0] = owner + owner.PosIndex = 0 for i := 0; i < int(robotNum); i++ { table.Robots[i] = &game.Robot{} @@ -158,10 +159,12 @@ func HandleReqJoinTable(account *session.NetAccount, msg *api.ReqJoinTable) (pro ProtoWriter: account.Client, } player.Init() - err := table.JoinPlayer(player) + index, err := table.JoinPlayer(player) if err != nil { return nil, err } + player.PosIndex = index + // 从账户扣除进入牌桌的金额 account.DecrementBalance(table.LimitInAmount) player.Chip = table.LimitInAmount @@ -393,15 +396,6 @@ func HandleReqGameStart(player *game.Player, msg *api.ReqGameStart) (proto.Messa // 玩家状态置为等待 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) @@ -453,7 +447,7 @@ func HandleReqGameStart(player *game.Player, msg *api.ReqGameStart) (proto.Messa return &api.ResSuccess{}, nil } -// HandleReqPlaceBetChip 下注 +// HandleReqPlaceBet 下注 // 网易德州扑克:http://sports.163.com/special/poker_rule/?ivk_sa=1025883k // 天天德州: 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 @@ -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://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 -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() defer player.Lock.Unlock() player.GameTable.ChipLock.Lock() @@ -470,62 +468,143 @@ func HandleReqPlaceBetChip(player *game.Player, msg *api.ReqPlaceBetChip) (proto if !collect.In(player.Status, 4, 6) { return &api.ResFail{Msg: "当前未轮到您下注"}, nil } - switch player.Status { - case 4: // 小盲注下注,下注金额最小大盲注1半, - if msg.BetChip < player.GameTable.BigBlindChip/2 { - return &api.ResFail{Msg: fmt.Sprintf("您当前最小下注金额%d", player.GameTable.BigBlindChip/2)}, nil + if !collect.In(msg.BetType, player.BetOpts...) { + return &api.ResFail{Msg: fmt.Sprintf("当前不可执行该操作#%d", msg.BetType)}, 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: // 轮流下注,下注金额最小大盲注 - if msg.BetChip < player.GameTable.BigBlindChip { - return &api.ResFail{Msg: fmt.Sprintf("您当前最小下注金额%d", player.GameTable.BigBlindChip)}, nil + fallthrough + case 2: // 加注 + if table.LastPosBetChip < 0 { + return &api.ResFail{Msg: "您当前只能All-In或弃牌"}, nil } - default: - return &api.ResFail{Msg: "当前未轮到您下注"}, nil - } - // 检查最大下注金额 - var maxBetChip int32 = 0 - // 依赌场规则而异, - // 若所余筹码All-in后仍低于最低加注金额,则此注仅视为跟注(call)而不能被当成加注(raise),亦及该圈若未有其他人再加注,则原加注者(此例中的第一位牌手)仅可跟注补齐或盖牌,于此圈不可再行加注 - // 亦有少部分赌场规定All-in后大于最低加注额的一半以上,原加注者即可重新加注。 + if msg.BetChip < player.BetMin { + return &api.ResFail{Msg: fmt.Sprintf("小于当前最低投注额#%d", player.BetMin)}, nil + } + if player.BetMax > 0 && msg.BetChip > player.BetMax { + return &api.ResFail{Msg: fmt.Sprintf("大于当前最大投注额#%d", player.BetMax)}, nil + } + // 限注式加注3次判断 + 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: - return &api.ResFail{Msg: "牌桌"}, nil + return &api.ResFail{Msg: fmt.Sprintf("未知操作#%d", msg.BetType)}, nil } - if player.GameTable.TexasType == 3 { - // 无限制可All In - allIn = msg.BetChip == maxBetChip - - } else if player.Status == 4 { - // 最大金额减去小盲注金额 - maxBetChip -= player.GameTable.BigBlindChip / 2 - } - if msg.BetChip > maxBetChip { - return &api.ResFail{Msg: fmt.Sprintf("您当前最大下注金额%d", maxBetChip)}, nil + // 通知下注结果 + if line1 != "" { + notice := &api.ResNoticePlayerLine{PlayerId: player.Id, Line1: line1, Line2: line2} + player.GameTable.NoticeAllPlayer(notice) } - 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 弃牌 -func HandleReqBetDiscard(player *game.Player, msg *api.ReqBetDiscard) (proto.Message, error) { +// playerWin 玩家获胜,结算牌局 +func playerWin(player *game.Player) { - return nil, nil } diff --git a/tool/collect/collect.go b/tool/collect/collect.go index aa83956..cee3547 100644 --- a/tool/collect/collect.go +++ b/tool/collect/collect.go @@ -8,3 +8,23 @@ func In[T comparable](value T, values ...T) bool { } 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 +}