我参加了2015华为软件精英挑战赛,在独立完成代码编写和调试的过程中,自己的编程能力也有了一些进步。现把所有代码分享如下,希望大家能批评指正。
项目简介:依据德州扑克规则,使用C++编写牌手程序,参加扑克比赛。
程序语言:C++ 通讯方式:TCP socket通讯
开发工具:g++ 开发环境:Linux操作系统Ubuntu
完成内容:
在linux下,实现了与服务器程序的TCP socket通信
处理粘包问题,实现了文本消息的无差错提取
完成牌型判断,对手加注风格分析等程序设计
完成了加注策略的算法设计及其C++实现
为了提交代码方便,我把所有子文件都整理到一个文件中:
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
//以上是linux下socket的头文件
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <map>
#include<cstring>
#include <cstdlib>
#include <fstream>//读写文件的头文件
using namespace std;
typedef multimap<int,int> MMAP;
MMAP mycard;
// 5个子函数声明 ////////////////////////////////////////////////////////////////////////////////
int hold_msg(MMAP& mycard, char* buffer,int& mycard_score,int& handcard_color,int& handcard_point1,int& handcard_point2);
int flop_msg(MMAP& mycard, char* buffer,int& mycard_score,int handcard_color,int handcard_point1,int handcard_point2);
int turn_msg(MMAP& mycard, char* buffer,int& mycard_score,int handcard_color,int handcard_point1,int handcard_point2);
int river_msg(MMAP& mycard, char* buffer,int& mycard_score,int handcard_color,int handcard_point1,int handcard_point2);
int inquire_msg(char* buffer,int& mybet,int& allfold,MMAP mycard,int hand_num,int& raise_pid_num,int& jijing);
////////////////////////////////////////////////////////////////////////////////
int mycard_score = 0;//手牌分数
int mybet = 1100;//离我最近的第一个没fold的人的本手牌累计投注额,意味着我要继续玩下去,一共要投入的钱
int allfold = 0;//1表示我前面的人都弃牌了
int parameter1=70;//起始赌注界限,是否大小盲注决定
int allmybet[100];//记录赌注是否曾经了,用来判断是不是不需要下赌注就可以混下去
int betnum=1;//数组allmybet的计数器
int bet_increase=1;//1表示赌注有增加
int handcard_color=1;//0表示两张手牌颜色不同,这时同花没有意义
int handcard_point1=0;
int handcard_point2=1;
int jijing=1;//1表示有激进者存在
int raise_pid_num=0;
int hand_num=1;//当前是第几局
////////////////////////////////////////////////////////////////////////////////
////////// 下面开始socket连接通信 //////////////////////////////////////////
int mysocket_id = -1;//初始化为-1
/*ipparameter_num是命令行总的参数个数
ip_parameter[]是ipparameter_num个参数,其中第0个参数是程序的全名,以后的参数
命令行后面跟的用户输入的参数
示例./game 192.168.0.1 1024 192.168.0.2 2048 6001
其中 192.168.0.1 是牌桌程序IP, 1024 是牌桌程序端口号
其中 192.168.0.2 是牌手程序绑定的IP, 2048是牌手程序绑定的端口号
其中 6001 是用来向牌桌注册的ID
*/
int main(int ipparameter_num, char *ip_parameter[])//指针数组
{
in_addr_t server_ip = inet_addr(ip_parameter[1]);//其中 192.168.0.1 是牌桌程序IP
in_port_t server_port = htons(atoi(ip_parameter[2]));//1024 是牌桌程序端口号//htons():主机字节顺序转换为网络字节顺序(对无符号短型进行操作 4 bytes)
in_addr_t my_ip = inet_addr(ip_parameter[3]);//其中 192.168.0.2 是牌手程序绑定的IP
in_port_t my_port = htons(atoi(ip_parameter[4]));//2048是牌手程序绑定的端口号
int my_id = atoi(ip_parameter[5]);//其中 6001 是用来向牌桌注册的ID
/* 创建socket */
mysocket_id = socket(AF_INET, SOCK_STREAM, 0);//AF_INET表示ipv4协议,AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合
if(mysocket_id < 0)/* 如果发生错误,socket()函数返回 – 1 */
{
printf("init socket failed!\n");
return -1;
}
/* 设置socket选项,地址重复使用,防止程序非正常退出,下次启动时bind失败 */
int rep = 1;
setsockopt(mysocket_id, SOL_SOCKET, SO_REUSEADDR, (const char*)&rep, sizeof(rep));//(const char*)
/*struct sockaddr_in { //ipv4的地址结构
short int sin_family; Internet地址族
unsigned short int sin_port; 端口号
struct in_addr sin_addr; Internet地址
unsigned char sin_zero[8]; 添0(和struct sockaddr一样大小)
}*/
struct sockaddr_in my_addr;//sockaddr_in是在头文件中定义的结构体
bzero(&my_addr, sizeof(my_addr));//把 sin_zero 全部设成零值
my_addr.sin_family = AF_INET;//给ipv4地址结构的三个量赋值
my_addr.sin_addr.s_addr = my_ip;
my_addr.sin_port = my_port;
//当你使用socket()函数得到一个套接字描述符,你也许需要将 socket绑定上一个你的机器上的端口。
if(bind(mysocket_id, (struct sockaddr*)&my_addr, sizeof(my_addr)))//bind()函数把一个地址族中的特定地址赋给socket描述符
{
printf("bind failed!\n");
return -1;
}
/* 连接server */
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = server_ip;
server_addr.sin_port = server_port;
//connect函数,客户端通过调用connect函数来建立与TCP服务器的连接。
while(connect(mysocket_id, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
{
usleep(100*1000); /* sleep 100ms, 然后重试,保证无论server先起还是后起,都不会有问题 */
}//usleep功能把进程挂起一段时间,单位是微秒; sleep()里面的单位是秒,而不是毫秒
/* 向server注册 */
char send_msg[50] = {
'\0'};
snprintf(send_msg, sizeof(send_msg)-1, "reg: %d %s \n", my_id, "fuliguo");
send(mysocket_id, send_msg, strlen(send_msg)+1, 0);
/* 接收server消息,进入游戏 */
//*************************************************************************************************************************************************************/
//******** *************************************************************************************************************/
//******** 下面是我的判断和动作 *************************************************************************************************************/
//******** *************************************************************************************************************/
//*************************************************************************************************************************************************************/
while(1)
{
//接收buffer
char buffer[1024] = {
'\0'};
int length = recv(mysocket_id, buffer, sizeof(buffer)-1, 0);
//printf("buffer:\n\n%s\n\n",buffer);
fflush(stdout);
//buffer有值
if(length > 0)
{ //if(strstr(buffer,"win")) {mycard.clear();}//在river里面清空可能更合适,注意win可能和hold消息在一个buffer里,
//如果win和river在一起怎么办,也不能在river里清空,因为有的局没有river.river和hold会连一起
///////////////////从收到的字符串中拆解出有用的几个信息//////////////////////////////////////
//首先要解决上一局的turn,river,win和这一局的seat,hold连起来的情况,因为这样会凑成4,7,8,9张牌,很容易出大牌而犯allin的大错误
if((strstr(buffer,"hold"))&&(strstr(buffer,"river"))&&(strstr(buffer,"win"))&&(strstr(buffer,"seat")))
{
//seat_msg(buffer,my_id,raise_pid_num,hand_num,changfold_flag,changcheck_flag,jijing);
mycard.clear();//在hold的一开始把mycard清空
hold_msg(mycard,buffer,mycard_score,handcard_color,handcard_point1,handcard_point2);
memset(allmybet,1,100);
betnum=1;
bet_increase=1;
}
else
{
if(strstr(buffer,"hold")) //在hold的一开始把mycard清空
{
mycard.clear();
hold_msg(mycard,buffer,mycard_score,handcard_color,handcard_point1,handcard_point2);
memset(allmybet,1,100);
betnum=1;
bet_increase=1;
}
if(strstr(buffer,"flop"))
{
flop_msg(mycard,buffer,mycard_score,handcard_color,handcard_point1,handcard_point2);
//flop_score=mycard_score;
}
if(strstr(buffer,"turn"))
{
turn_msg(mycard,buffer,mycard_score,handcard_color,handcard_point1,handcard_point2);
//turn_score=mycard_score;
}
if(strstr(buffer,"river"))
{
river_msg(mycard,buffer,mycard_score,handcard_color,handcard_point1,handcard_point2);
//river_score=mycard_score;
}
}
//////////////////////////////////////////////////////////////////////////////////////
if(strstr(buffer,"inquire"))
{
inquire_msg(buffer,mybet,allfold,mycard,hand_num,raise_pid_num,jijing);//截取消息,获得我继续玩将要投下的赌注总数
//设置个存放mybet的数组,每局清空一次,用来判断这次的mybet是否比上次增加了,要是没增加,就说明check就可以,不用下注,就没必要弃牌了
if(betnum>99)betnum=1;
else betnum += 1;
allmybet[betnum]=mybet;
if(allmybet[betnum]==allmybet[betnum-1]) bet_increase=0;
else bet_increase=1;
//if(betnum>50)betnum=0;else betnum += 1;
// 现在有手牌的分数了,根据 分数 和 mybet 两个参数做出action
//send_msg[50] = {'\0'};
memset(send_msg,'\0',50);
if(allfold==1) snprintf(send_msg,sizeof(send_msg) - 1,"check");
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
else if(jijing==0)
{
if(mycard.size()==2)
{
if(mybet>300)
{
if(bet_increase==0) snprintf(send_msg,sizeof(send_msg) - 1,"check");
else snprintf(send_msg,sizeof(send_msg) - 1,"fold");
}
else if(mybet>200)
{
if(mycard_score>3) snprintf(send_msg,sizeof(send_msg) - 1,"check");
else if(bet_increase==0) snprintf(send_msg,sizeof(send_msg) - 1,"check");
else snprintf(send_msg,sizeof(send_msg) - 1,"fold");
}
else if(mybet>79)
{
if(mycard_score>2) snprintf(send_msg,sizeof(send_msg) - 1,"check");
else if(bet_increase==0) snprintf(send_msg,sizeof(send_msg) - 1,"check");
else snprintf(send_msg,sizeof(send_msg) - 1,"fold");
}
else
{
if(mycard_score>2) snprintf(send_msg,sizeof(send_msg) - 1,"raise 43");
else if(mycard_score>0) snprintf(send_msg,sizeof(send_msg) - 1,"check");
else if(bet_increase==0) snprintf(send_msg,sizeof(send_msg) - 1,"check");
else snprintf(send_msg,sizeof(send_msg) - 1,"fold");
}
}
else
{
if(mybet>2000)
{
if(mycard_score>8) snprintf(send_msg,sizeof(send_msg) - 1,"check");//一定要有上限
else if(bet_increase==0) snprintf(send_msg,sizeof(send_msg) - 1,"check");
else snprintf(send_msg,sizeof(send_msg) - 1,"fold");
}
else if(mybet>1000)
{
if(mycard_score>5) snprintf(send_msg,sizeof(send_msg) - 1,"check");//一定要有上限
else if(bet_increase==0) snprintf(send_msg,sizeof(send_msg) - 1,"check");
else snprintf(send_msg,sizeof(send_msg) - 1,"fold");
}
else if(mybet>800)
{
if(mycard_score>4) snprintf(send_msg,sizeof(send_msg) - 1,"check");
else if(bet_increase==0) snprintf(send_msg,sizeof(send_msg) - 1,"check");
else snprintf(send_msg,sizeof(send_msg) - 1,"fold");
}
else if(mybet>300)
{
if(mycard_score>7) snprintf(send_msg,sizeof(send_msg) - 1,"raise 301");//一定要有上限
else if(mycard_score>5) snprintf(send_msg,sizeof(send_msg) - 1,"raise 43");
else if(mycard_score>3) snprintf(send_msg,sizeof(send_msg) - 1,"check");
else if(bet_increase==0) snprintf(send_msg,sizeof(send_msg) - 1,