德州扑克 2015 华为软件精英挑战赛

概述

华为2015软件挑战赛比赛总结,跟队友当时奋斗了15天吧,最后差点进32强了,第三轮遇到的对手太厉害,止步64强了。这次官方提供 Ubuntu 纯命令行镜像和庄家 Server,选手编写德州扑克选手机器人程序互相 PK(8人一组)500轮后钱多者胜出。运行只要运行华为提供的ne">dist_check_and_run.sh 脚本 即可,里面会给自动运行每个game程序.最近快要找工作了一些项目还是要总结一下的,感觉欠缺的地方还是有很多的,下面进入正题。

梳理一下主干主要这次比赛项目主要分为一下几大块:


1、Socket注册

开始pk选手向庄家Sever注册自己的信息,这里官方给出的信息作品的运行入口统一命名为game, 支持5个参数( 牌桌程序IP,牌桌程序端口好,牌手程序绑定的IP,牌手程序绑定的端口号,牌手的ID)
调用形式如下:
./game 192.168.0.1 1024 192.168.0.2 2048 6001

因此注册socket采用4参数的,输出字符流采用PrintWrite

// Socket link
			Socket socket = new Socket(args[0], Integer.parseInt(args[1]),
					InetAddress.getByName(args[2]), Integer.parseInt(args[3]));
			// OutputStream
			OutputStream os = socket.getOutputStream();
			PrintWriter pw = new PrintWriter(os);
			pw.write("reg: " + args[4] + " xsfelvis \n");
			pw.flush();

这里 PrintWriter提供了PrintStream的所有打印方法,其方法也从不抛出IOException。与PrintStream的区别:作为处理流使用时,PrintStream只能封装OutputStream类型的字节流,而PrintWriter既可以封装OutputStream类型的字节流,还能够封装Writer类型的字符输出流并增强其功能。 这样我们就成功的跟Sever服务器通信上了,(但是不要立马关闭socekt,需要接受Sever后续消息,读取到game over消息时关闭)迈出了第一步


2、解析消息

这里庄家Sever都会在一定的时候向选手发出公共消息,这个在比赛给出的程序通信协议中均有给出,主要包括

"seat/ " 座次
"blind/ "盲注
"hold/ "手牌
"flop/ "公牌
"turn/ "转牌
"river/ "河牌
"inquire/ "询问消息(即是否出牌/check)
"showdown/ "摊牌消息
"pot-win/ " 彩池信息

这些消息都需要进行正确提取作为自己判断的依据,主要先用hashmap将这些信息进行映射,以便于switch对应的信息元素;存储消息采用了StringBuffer,

这些消息都是每一轮的临时消息,但是需要再每一轮结束时都要清空(/pot-win),确保这是最新的一轮最新消息,否则会影响出牌判断。解析消息代码如下

final HashMap<String, Integer> MsgCommand = new HashMap<String, Integer>();
		MsgCommand.put("seat/ ", 1);// seat flag
		MsgCommand.put("blind/ ", 2);// bind flag
		MsgCommand.put("hold/ ", 3);// hold flag
		MsgCommand.put("flop/ ", 4);// flop flag
		MsgCommand.put("turn/ ", 5);// turn flag
		MsgCommand.put("river/ ", 6);// river flag
		MsgCommand.put("inquire/ ", 7);// inquire flag
		MsgCommand.put("showdown/ ", 8);// showdown flag
		MsgCommand.put("pot-win/ ", 9);// pot flag
		int ServerCommand;
		try {
			// Socket link
			Socket socket = new Socket(args[0], Integer.parseInt(args[1]),
					InetAddress.getByName(args[2]), Integer.parseInt(args[3]));
			// OutputStream 向Sever发送注册消息
			OutputStream os = socket.getOutputStream();
			PrintWriter pw = new PrintWriter(os);
			pw.write("reg: " + args[4] + " xsfelvis \n");
			pw.flush();
			// InputStream 接受Sever的消息
			InputStream is = socket.getInputStream();
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			// define Msg StringBuffer 存储消息
			StringBuffer SeatMsg = new StringBuffer("");
			StringBuffer BlindMsg = new StringBuffer("");
			StringBuffer CardsMsg = new StringBuffer("");
			StringBuffer InquireMsg = new StringBuffer("");
			StringBuffer ShowdownMsg = new StringBuffer("");
			StringBuffer PotWinMsg = new StringBuffer("");
			String info;
			String ActMsg;
			int Seat_num = 0;
			//开始消息解析存储到对应的StringBuffer中
			while (true) {
				info = br.readLine();// get a line of info
				ServerCommand = MsgCommand.get(info);// get hashmap value
				switch (ServerCommand) {
				case 1: {
					while (info != null) {
						info = br.readLine();
						if (info.equals("/seat "))
							break;
						Seat_num++;
						SeatMsg.append(info);
					}
					break;
				}
				case 2: {
					while (info != null) {
						info = br.readLine();
						if (info.equals("/blind "))
							break;
						BlindMsg.append(info);
					}
					break;
				}
				case 3: {
					while (info != null) {
						info = br.readLine();
						if (info.equals("/hold "))
							break;
						CardsMsg.append(info);
					}
					break;
				}
				case 4: {
					while (info != null) {
						info = br.readLine();
						if (info.equals("/flop "))
							break;
						CardsMsg.append(info);
					}
					break;
				}
				case 5: {
					while (info != null) {
						info = br.readLine();
						if (info.equals("/turn "))
							break;
						CardsMsg.append(info);
					}
					break;
				}
				case 6: {
					while (info != null) {
						info = br.readLine();
						if (info.equals("/river "))
							break;
						CardsMsg.append(info);
					}
					break;
				}
				case 7: {
					while (info != null) {
						info = br.readLine();
						if (info.equals("/inquire "))
							break;
						InquireMsg.append(info);
					}
					break;
				}
				case 8: {
					while (info != null) {
						info = br.readLine();
						if (info.equals("/showdown "))
							break;
						ShowdownMsg.append(info);
					}
					break;
				}
				case 9: {
					while (info != null) {
						info = br.readLine();
						if (info.equals("/pot-win "))
							break;
						PotWinMsg.append(info);
					}
					break;
				}
				default:
					break;
				}
				if (info.equals("/inquire ")) {
					//出牌算法 核心所在!
					ActMsg = Alg(CardsMsg.toString(), InquireMsg.toString(),
							args[4], Seat_num);
					InquireMsg = new StringBuffer("");
					pw.write(ActMsg);
					pw.flush();
				}
				if (info.equals("/pot-win ")) {
					Seat_num = 0;
					SeatMsg = new StringBuffer("");
					BlindMsg = new StringBuffer("");
					CardsMsg = new StringBuffer("");
					ShowdownMsg = new StringBuffer("");
					PotWinMsg = new StringBuffer("");
				}
				if (info.equals("game-over ")) {
					br.close();
					isr.close();
					pw.close();
					os.close();
					socket.close();
					break;
				}
			}

		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

补充:

这里由于Sever每次发送 一串字符串,开头和结尾都有特定的标志,而且都是双方约定好的字符串,因而无需考虑TCP的粘包问题。

3 、算法部分

用一句话总结一下就是“只玩大牌,小牌能check就check,不能则fold”所谓的大牌主要参考了百度一篇《德州扑克最佳技巧》

德州扑克最佳技巧

感觉算法部分不是很好没有做太多的优化,也没有基于对手建立相应的数学模型或者是加入博弈论的知识,就不展示了

详细代码下载




你可能感兴趣的:(华为)