430 likes | 652 Views
基于 linux 下的网络型五子棋. 嵌入式课程设计之. 涉及提纲. 基于 linux 下的网络型五子棋 系统简介 MiniGUI 下的窗口、菜单及消息框的介绍 本地人人对抗 人机对抗 网络人人对抗. 系统介绍. 通过嵌入式系统平台上的触摸屏和 LCD 屏实现本地人人对抗、人机对抗和联网人人对抗三种模式的五子棋,并伴随着优雅的音乐,以增强娱乐性。. 硬件平台. 基于 intel 公司的 xscale PXA272 实验平台. 功能 :. 本地人人对抗 在嵌入式平台上,基于触摸屏进行人与人之间的对抗 。 人机对抗
E N D
基于linux下的网络型五子棋 嵌入式课程设计之
涉及提纲 • 基于linux下的网络型五子棋系统简介 • MiniGUI下的窗口、菜单及消息框的介绍 • 本地人人对抗 • 人机对抗 • 网络人人对抗
系统介绍 通过嵌入式系统平台上的触摸屏和LCD屏实现本地人人对抗、人机对抗和联网人人对抗三种模式的五子棋,并伴随着优雅的音乐,以增强娱乐性。
硬件平台 • 基于intel 公司的xscale PXA272实验平台
功能: • 本地人人对抗 在嵌入式平台上,基于触摸屏进行人与人之间的对抗 。 • 人机对抗 添加相应的人工智能算法,实现人与嵌入式平台之间对抗的功能。 • 网络人人对抗 基于tcp/ip的网络间对抗
涉及技术: Linux系统开发、MiniGui图形界面开发技术、搭建局域网络、socket网络编程技术、多线程技术、串口操作、音视频播放技术,人工智能算法。
MiniGUI概述 • 面向嵌入式系统和实时系统的用户界面支持系统 • 完备的多窗口机制 • 对话框和预定义的控件类 • 事件驱动机制 • 多字符多字体支持
MiniGUI运行模式 • MiniGUI-Threads:可在不同线程中建立多个窗口,但这些窗口均在一个进程中或地址空间中运行 • MiniGUI-Lite:每个程序是单独的进程,每个进程可创建多个窗口 • MiniGUI-Standalone:可以独立进程方式运行,不需多线程的支持
主窗口 • MiniGUI中的主窗口没有窗口类的概念,应通过初始化一个MAINWINCREATE结构,然后调用CreateMainWindow函数来创建一个主窗口 • HWND hMainWnd; • MAINWINCREATE CreateInfo; • CreateInfo.dwStyle=… • …… • hMainWnd = CreateMainWindow (&CreateInfo);
主窗口的销毁 • 1)关闭主窗口 • 2)主窗口过程接收到MSG_CLOSE消息 • 3)处理如下: case MSG_CLOSE: DestroyMainWindow(hWnd); PostQuitMessage(hWnd); return 0; • 4) while(GetMessage(&Msg,hMainWnd)) { ... } • DestroyMainWindow销毁一个主窗口,但不会销毁主窗口所使用的消息队列以及窗口对象本身。 • 因此,应用程序要在线程或进程的最后使用MainWindowCleanup最终清除主窗口所使用的消息队列以及窗口对象本身。
菜单 • 菜单通常依附于窗口中(称为普通菜单),或者以独立的、可弹出形式出现(称为弹出式菜单)。主要是提供给用户一种快捷选择的方式。
创建普通菜单 • 在程序中,我们首先要建立菜单,然后将菜单句柄传递给创建主窗口的函数。当主窗口显示出来时,我们创建的菜单将会在标题栏下显示出来。当用户用鼠标激活菜单并选择了菜单项后,该菜单所依附的窗口会收到MSG_COMMAND消息。 • 菜单创建需要两个过程: • 建立菜单栏; • 建立菜单栏中各个菜单的子菜单。
HMENU createpmenuabout_chess(void) • { • HMENU hmnu; • MENUITEMINFO mii; • memset (&mii, 0, sizeof(MENUITEMINFO)); • mii.type = MFT_STRING; • mii.id = 0; • mii.typedata = (DWORD)"About"; • hmnu = CreatePopupMenu (&mii); • memset (&mii, 0, sizeof(MENUITEMINFO)); • mii.type = MFT_STRING ; • mii.state = 0; • mii.id = GAME_ABOUT; • mii.typedata = (DWORD)"About Gobang"; • InsertMenuItem(hmnu, 3, TRUE, &mii); • return hmnu; • }
MessageBox的函数说明 • 消息框是用于给用户一些提示或警告的主窗口,属于内建的对话框类型。 • 函数原型为int GUIAPI MessageBox (HWND hParentWnd, const char* pszText, const char* pszCaption, DWORD dwStyle) • 这个函数是可以显示带有几个按钮的消息框,程序可以通过用户点击不同的按钮来获得不同的返回值,从而进行一些操作。其中HWND hParentWnd为主窗口的句柄,const char* pszText为显示在消息框中的文字提示信息,const char* pszCaption为消息框的名称,DWORD dwStyle为消息框的类型,例如有几个按钮等。
本程序中的一个消息框函数举例 • MessageBox(hwnd, "New game? Vs Computer!", "Gobang", MB_OK | MB_OKCANCEL | MB_ICONQUESTION) == IDOK
Game菜单举例 New game? Vs Human! New game? Vs Computer! Online game? Are you sure to quit?
人人对战(在嵌入式平台上,基于触摸屏进行 ) • 首先选择开始游戏,进入人人对战模式,然后黑子和白子依次按照顺序落子,每一方落子之后,会进行相应的判断,在此子的八个方向上有没有连续的五个同色棋子,如果有则此方获胜,显示相应的提示信息,游戏结束。如果没有,则对方继续落子,直到一方获胜为止。
在本程序的此模式下设有悔棋的功能,具体函数流程图如下:在本程序的此模式下设有悔棋的功能,具体函数流程图如下: 否‘ 是 是 否
人机对战中的电脑智能 • 人机对战模式中,电脑的人工智能是本项目中算法的难点,怎样平衡功守,让电脑的下棋不至于象傻瓜一样,并有一定的进攻性,有许多要考虑的情况。 • 以攻为主,功守兼备
总体流程 人下棋 是 人方胜利 宣布人方胜利 否 扫描整个棋盘,找最高优先级 在适当位置下子
棋局分析 此优先级可设置成1(最高)
棋局分析 优先级也是1
棋局分析 优先级为2
棋局分析 优先级为3
棋局分析 优先级为4
棋局分析 优先级为5
棋局分析 优先级为6
背景音乐的添加 • Exec函数族 • Int execl(const char *path,const char *arg,…) • const char *path 参数是路径名 • path,const char *arg 为参数列表 • execl("/host/madplay","madplay","fuyu.mp3")
背景音乐的添加 • 最初打算用多线程 • 结果。。。死机 • 线程间的资源占用问题 • 用多进程实现。 • if(fork()==0) { execl("/host/madplay","madplay","fuyu.mp3"); exit(0); }
网络人人对抗发表流程 • 搭建网络连接 • 本方下棋并发送棋子信息 • 接收棋子信息并处理
搭建网络连接 • 服务器端: • 客户端:
服务器端 • 首先服务器端程序创建了一个永久的套接口监听服务请求;当客户机连接到服务器时,就建立了一个临时套接口。每次客户机连接到服务器,一个临时套接口就在客户机和服务器之间打开。接下来的数据既支持为客户连接创建的永久套接口,又支持临时套接口: struct sockaddr_in my_addr; struct sockaddr_in their_addr; int sin_size;
首先创建一个socket通信,并获得socket描述符: sock_fd = socket(AF_INET,SOCK_STREAM,0); • 接下来填写结构sockaddr_in必要的域sin: my_addr.sin_family=AF_INET; my_addr.sin_port = htons(MYPORT); my_addr.sin_addr.s_addr = 0; bzero(&(my_addr.sin_zero),8); • 下面我们可以使用函数bind将套接口和端口2000连在一起: bind(sock_fd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr)
最后我们开始监听连接到2000端口的新套接口:(服务器端使用)最后我们开始监听连接到2000端口的新套接口:(服务器端使用) listen(sock_fd,BACKLOG) ; • new_fd用来接收客户端的socket连接,服务器端的程序会一直阻塞到有一个客户程序发出了连接. 其返回值则作为新的socket处理代码,供send,recv函数使用。 new_fd = accept(sock_fd,(struct sockaddr*)&their_addr,&sin_size); • 当该调用成功返回后,我们即建立起来了网络连接。
客户端 • 为了监听服务器端的发送服务请求,客户端建立了一个临时套接口,以下的数据是用来建立临时套接口与服务器的连接: int sock_fd; struct sockaddr_in their_addr; • 客户端程序必须知道主机的IP地址,当我们知道主机的IP地址后,就可以对结构sockaddr_in中的数据pin赋值: their_addr.sin_family=AF_INET; their_addr.sin_port = htons(MYPORT); their_addr.sin_addr.s_addr = inet_addr("192.168.0.2"); bzero(&(their_addr.sin_zero),8);
建立与主机进行套接口连接的工作已经准备就绪:建立与主机进行套接口连接的工作已经准备就绪: sock_fd=socket(AF_INET,SOCK_STREAM,0); • 最后我们使用该套接口连接到主机: connect(sock_fd,(struct sockaddr *)&their_addr,sizeof(struct sockaddr) ; • 当服务器忙,本次调用可能会阻塞(等待)。当对connect的调用成功返回时,我们就完成了网络的连接。 • 当我们建立好网络连接后,服务器端和客户端就可以互相通信了。
服务器端 socket-->bind-->listen-->accept--> recv、send • 客户端 socket-->connect--> recv、send
本方下棋并发送棋子信息 • 搭建好网络连接以后,我们就可以开始网络人人对抗游戏。在此,我们默认服务器端执黑棋,客户端执白棋,并且默认开始网络人人对抗新游戏时,总是服务器端(执黑子者)先下子。在此我们需要定义一个网络标示位flag_online,用于提示和监视该哪方下子。在这个小模块中,服务器端和客户端原理基本一样,在此,我们仅以服务器端为例,首先,根据网络标示位flag_online来判断黑子是否能下子,若能下,则服务器端执黑棋下子。
当下子以后,程序进行判断,判断点击位置是否在有效范围内,若无效,则不绘棋子。当有效时,经计算后在相应的位置绘出黑子。此后,立即发送该黑子的信息(位置)。同是对方接收到该黑子的位置信息后,进行相应的处理,在第三部分详细说明。然后,调用棋子判断输赢算法,若黑子赢,给出提示信息,然后重新开始游戏。若黑子不赢,则置网络标示位flag_online = 0,此时,在等待对方下子,在对方下子之前,禁止黑子下子。
点击按键 N 允许黑子下 Y 拆分点击位置的x,y坐标 Y N 坐标是否有效 发送该位置信息 黑子是否赢 N Y 置标志位 flag_online=0 给出提示 初始化棋盘 退出 下棋并发送下棋信息流程图信息
接收棋子信息并处理 • 线程:是在共享内存空间中并发的多道执行路径,他们共享一个进程的资源,如文件描述符和信号处理。线程能够检查程序中潜在的并行性,能够找出同时执行的任务。我们知道linux的进程模型已经提供了执行多个进程的能力,因而已经可以进行并行或并发编程。但是线程能够让你对多个任务的控制程度更好,使用的系统资源更少,因为一个单一的资源,可以由多个线程共享。在拥有多个CPU的系统上,多线程应用会比用多个进程实现的应用执行的速度更快。综上所述我们采用了多线程技术。
MSG_MY消息响应 接收对方信息线程 在本方棋盘显示 N 是否接收到数据 对方是否赢棋 Y flag_online = 1 给出提示信息 初始化棋盘 发送消息给主线程 等待本方下子 MSG_MY消息响应流程图 接收对方信息线程流程图 接收棋子信息并处理流程图