概述
华为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”所谓的大牌主要参考了百度一篇《德州扑克最佳技巧》
感觉算法部分不是很好没有做太多的优化,也没有基于对手建立相应的数学模型或者是加入博弈论的知识,就不展示了
版权声明:本文为博主原创文章,未经博主允许不得转载。