Игра “Крестики-нолики”-это логическая игра,целью которой является расставить 5 крестиков или 5 ноликов в один ряд по горизонтали,по вертикали или по горизонтали.Кто расставил,тот и выйграл.Игра происходит между двумя игроками по локальной сети.При загружении игры один из игроков ставит галочку в меню communication->server,другой же в меню communication->client,вписывает IP адресс сервера и нажимает connect.Далее оба игрока выбирают новую игру(Game->New) и наслаждаются игрой.
Screenshot:
Постановка задачи.
Разработать клиент-серверное приложение, в данном случае игру “Крестики-нолики”, состоящий из клиента и сервера, позволяющего посылать и принимать информацию.Немного теории.
Применяемая в IP-сетях архитектура клиент-сервер использует IP-пакеты для коммуникации между клиентом и сервером. Клиент отправляет запрос серверу, на который тот отвечает. В случае с TCP/IP между клиентом и сервером устанавливается соединение (обычно с двусторонней передачей данных), а в случае с UDP/IP - клиент и сервер обмениваются пакетами (дейтаграммамми) с негарантированной доставкой.
Каждый сетевой интерфейс IP-сети имеет уникальный в этой сети адрес (IP-адрес). Упрощенно можно считать, что каждый компьютер в сети Интернет имеет собственный IP-адрес. При этом в рамках одного сетевого интерфейса может быть несколько сетевых портов. Для установления сетевого соединения приложение клиента должно выбрать свободный порт и установить соединение с серверным приложением, которое слушает (listen) порт с определенным номером на удаленном сетевом интерфейсе. Пара IP-адрес и порт характеризуют сокет (гнездо) - начальную (конечную) точку сетевой коммуникации. Для создания соединения TCP/IP необходимо два сокета: один на локальной машине, а другой - на удаленной. Таким образом, каждое сетевое соединение имеет IP-адрес и порт на локальной машине, а также IP-адрес и порт на удаленной машине.
Модуль socket обеспечивает возможность работать с сокетами из Python. Сокеты используют транспортный уровень согласно семиуровневой модели OSI (Open Systems Interconnection, взаимодействие открытых систем), то есть относятся к более низкому уровню, чем большинство протоколов.
Уровни модели OSI:
Физический
Поток битов, передаваемых по физической линии. Определяет параметры физической линии.
Канальный (Ethernet, PPP, ATM и т.п.)
Кодирует и декодирует данные в виде потока битов, справляясь с ошибками, возникающими на физическом уровне в пределах физически единой сети.
Сетевой (IP)
Маршрутизирует информационные пакеты от узла к узлу.
Транспортный (TCP, UDP и т.п.)
Обеспечивает прозрачную передачу данных между двумя точками соединения.
Сеансовый
Управляет сеансом соединения между участниками сети. Начинает, координирует и завершает соединения.
Представления
Обеспечивает независимость данных от формы их представления путем преобразования форматов. На этом уровне может выполняться прозрачное (с точки зрения вышележащего уровня) шифрование и дешифрование данных.
Приложений (HTTP, FTP, SMTP, NNTP, POP3, IMAP и т.д.)
Поддерживает конкретные сетевые приложения. Протокол зависит от типа сервиса.
Каждый сокет относится к одному из коммуникационных доменов. Модуль socket поддерживает домены UNIX и Internet. Каждый домен подразумевает свое семейство протоколов и адресацию. Данное изложение будет затрагивать только домен Internet, а именно протоколы TCP/IP и UDP/IP, поэтому для указания коммуникационного домена при создании сокета будет указываться константа socket.AF_INET.
В качестве примера следует рассмотреть простейшую клиент-серверную пару. Сервер будет принимать данные о ходе клиента, и отвечать ему.
Реализация программы.
1 Структура программы.
Для начала определим класс сообщений (XO). В этом классе описаны почти все переменные и функции, с помощью которых реализуются выполнение программы. Это самый основной класс. Здесь идет работа с сокетами, обмен информацией между клиентом и сервером, анализ игры.
class XO : public CFrameWnd { public: void Send(char buf[bufsize]); bool Check(int i1,int i2,int i3,int i4,int i5); bool IsWon(); void CalcCellRect(); void DrawFrame(CPaintDC*); XO(); private: CString sAddr; CMenu menu; CPen pen; Cell cells[25]; mode cur_mod; bool bConnected; bool bGame; bool bServer; bool bClient; int num_cells; bool mymove; protected: protected: virtual BOOL PreCreateWindow(CREATESTRUCT& cs); protected: afx_msg void OnConnectClient(); afx_msg void OnPaint(); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnGameNew(); afx_msg void OnGameExit(); afx_msg void OnClose(); afx_msg void OnConnectServer(); afx_msg void OnUpdateConnectClient(CCmdUI* pCmdUI); afx_msg void OnUpdateConnectServer(CCmdUI* pCmdUI); afx_msg void OnUpdateGameNew(CCmdUI* pCmdUI); afx_msg LRESULT OnAsync(WPARAM wparam, LPARAM lparam); DECLARE_MESSAGE_MAP() }; |
struct Cell { mode mod; bool active; CRect place; Cell() {active = false;} void SetCell(mode m) {mod = m; active = true;} }; |
class Dlg : public CDialog { public: CString addr; Dlg(CWnd* p=NULL) : CDialog(IDD_CONDIALOG,p){} protected: virtual void DoDataExchange(CDataExchange* pDX); }; |
class MyApp : public CWinApp { public: virtual BOOL InitInstance() { AfxSocketInit(); m_pMainWnd = new XO; m_pMainWnd->ShowWindow(SW_SHOWNORMAL); return TRUE; } }; MyApp app; |
· Функция задаёт параметры окна.
BOOL XO::PreCreateWindow(CREATESTRUCT& cs) { cs.style &= ~WS_MAXIMIZEBOX; cs.style &= ~WS_THICKFRAME; cs.cx = 400; cs.cy = 400; return CFrameWnd::PreCreateWindow(cs); } LRESULT XO::OnAsync(WPARAM wParam,LPARAM lParam) { WSAEvent = WSAGETSELECTEVENT (lParam); switch (WSAEvent) { case FD_READ: if(bServer) recv(hSock2, buf, bufsize, 0); else recv(hSock, buf, bufsize, 0); if(buf[0]==33) { for(int i=0; i<25; i++) { cells[i].active=false; } cur_mod=X; bGame=true; mymove=false; num_cells=0; Invalidate(); return 0; } if(mymove==false) mymove=true; cells[buf[0]].active=true; cells[buf[0]].mod = buf[1]==1 ? X : O; cur_mod= cur_mod==X ? O : X; num_cells++; IsWon(); Invalidate(); return 0; case FD_ACCEPT: lenaddr=sizeof(myaddr); hSock2=accept(hSock,(LPSOCKADDR)&myaddr,&lenaddr); return 0; } return 1; } |
void XO::OnConnectClient() { bClient=true; WSAStartup(WS_VERSION_REQD, &stWSAData); hSock = socket (AF_INET, SOCK_STREAM, 0); Dlg dlg; dlg.DoModal(); sAddr = dlg.addr; myaddr.sin_family = AF_INET; myaddr.sin_addr.s_addr = inet_addr(sAddr); myaddr.sin_port = htons (2049); connect (hSock, (struct sockaddr *)&myaddr,sizeof(myaddr)); nRet=WSAAsyncSelect(hSock,m_hWnd,WM_ASYNC,FD_READ); bConnected=true; bServer=false; mymove=true; SetWindowText("xo - client"); } |
void XO::OnConnectServer() { bServer=true; WSAStartup(WS_VERSION_REQD, &stWSAData); hSock = socket (AF_INET,SOCK_STREAM,0); WSAAsyncSelect(hSock,m_hWnd,WM_ASYNC, FD_ACCEPT | FD_READ); myaddr.sin_family = AF_INET; myaddr.sin_addr.s_addr = htonl (INADDR_ANY); myaddr.sin_port = htons (2049); bind(hSock,(LPSOCKADDR)&myaddr, sizeof(struct sockaddr)); listen (hSock, 5); bConnected=true; bClient=false; mymove=false; SetWindowText("xo - server"); } |
void XO::Send(char buf[]) { if(bClient) send(hSock,buf,bufsize,0); else send(hSock2,buf,bufsize,0); } void XO::OnUpdateConnectClient(CCmdUI* pCmdUI) { pCmdUI->Enable(bClient); pCmdUI->SetCheck(int(bClient&&bConnected)); } void XO::OnUpdateConnectServer(CCmdUI* pCmdUI) { pCmdUI->Enable(bServer); pCmdUI->SetCheck(int(bServer&&bConnected)); } void XO::OnUpdateGameNew(CCmdUI* pCmdUI) { pCmdUI->Enable(bConnected); } |
void XO::OnPaint() { CPaintDC dc(this); dc.SelectObject(&pen); DrawFrame(&dc); for(int i=0; i<25; i++) { if(cells[i].active==true) { if(cells[i].mod==X) { dc.MoveTo(cells[i].place.left,cells[i].place.top); dc.LineTo(cells[i].place.right,cells[i].place.bottom); dc.MoveTo(cells[i].place.left,cells[i].place.bottom); dc.LineTo(cells[i].place.right,cells[i].place.top); } else if(cells[i].mod==O) { dc.Ellipse(cells[i].place); } } } } void XO::DrawFrame(CPaintDC* dc) { CRect r; GetClientRect(&r); dc->MoveTo(r.left,r.bottom/5); dc->LineTo(r.right,r.bottom/5); dc->MoveTo(r.left,2*r.bottom/5); dc->LineTo(r.right,2*r.bottom/5); dc->MoveTo(r.left,3*r.bottom/5); dc->LineTo(r.right,3*r.bottom/5); dc->MoveTo(r.left,4*r.bottom/5); dc->LineTo(r.right,4*r.bottom/5); dc->MoveTo(r.right/5,r.top); dc->LineTo(r.right/5,r.bottom); dc->MoveTo(2*r.right/5,r.top); dc->LineTo(2*r.right/5,r.bottom); dc->MoveTo(3*r.right/5,r.top); dc->LineTo(3*r.right/5,r.bottom); dc->MoveTo(4*r.right/5,r.top); dc->LineTo(4*r.right/5,r.bottom); } void XO::OnLButtonDown(UINT nFlags, CPoint point) { if(bGame==false || mymove==false) return; for(int i=0; i<25; i++) { if(cells[i].place.PtInRect(point) && !cells[i].active) { buf[0]=i; cells[i].active=true; cells[i].mod=cur_mod; mymove=false; num_cells++; InvalidateRect(cells[i].place); cur_mod= cur_mod== X ? O : X; buf[0]=i; buf[1]= cells[i].mod==X ? 1 : 0; Send(buf); break; } } IsWon(); } void XO::CalcCellRect() { CRect r; GetClientRect(&r); int cx = r.right; int cy = r.bottom; for(int j=0,t=r.top,b=r.top+cy/5; j<5; j++,t+=cy/5,b+=cy/5) { for(int i=0,l=r.left,p=r.left+cx/5; i<5; i++,p+=cx/5,l+=cx/5) { cells[j*5+i].place.left=l+5; cells[j*5+i].place.right=p-5; cells[j*5+i].place.top=t+5; cells[j*5+i].place.bottom=b-5; } } } |
·
bool XO::IsWon() { bool won=false; won = Check(0,1,2,3,4)||Check(4,9,14,19,24) ||Check(20,21,22,23,24)||Check(0,5,10,15,20) ||Check(1,6,11,16,21)||Check(2,7,12,17,22) ||Check(5,6,7,8,9)||Check(3,8,13,18,23) ||Check(10,11,12,13,14)||Check(15,16,17,18,19) ||Check(0,6,12,18,24)||Check(4,8,12,16,20); if(!won && num_cells==25) { MessageBox("None won ...",""); won=true; } if(won) bGame=false; return won; } |
void XO::OnGameNew() { if(bGame==true) return; for(int i=0; i<25; i++) { cells[i].active=false; } bGame=true; mymove=true; num_cells=0; cur_mod=X; buf[0]=33; Send(buf); Invalidate(); } |
bool XO::Check(int i1,int i2,int i3,int i4,int i5) { bool won=false; mode m; if(!(cells[i1].active && cells[i2].active && cells[i3].active && cells[i4].active && cells[i5].active)) return won; if((m=cells[i1].mod) == cells[i2].mod && cells[i2].mod == cells[i3].mod && cells[i3].mod == cells[i4].mod && cells[i4].mod == cells[i5].mod) { won=true; } if(won==true) { char who= m==X ? 'X' : 'O'; CString message="Victory of "; message += who; MessageBox(message,"Congratulations!"); } return won; } |
void XO::OnGameExit() { OnClose(); } |
void XO::OnClose() { nRet = WSACleanup(); CFrameWnd::OnClose(); } |
Комментариев нет:
Отправить комментарий