- 服务器端为电脑
- 客户端为人,可以选择先后手
- 编程环境为xcode,linux可运行,注意不要using namespac std,std::bind会与bind冲突
- 通过结构体Move交互,Move的masg字段存储终局信息,客户端根据这一字段是否为空判断是否结束游戏
- 电脑使用极小值极大值算法,至少平局,输赢局势判断都在服务器端,传状态给客户端
- 其中AbstractBoard.h 为基类
- 练手小玩意,其他棋牌可以用这种思想,客户端也可以进行输赢判断。
代码如下:
- AbstractBoard.h
//
// AbstractBoard.h
// TicTacToeServer
//
// Created by Tusko on 2017/4/10.
// Copyright © 2017年 Tusko. All rights reserved.
//
#ifndef AbstractBoard_h
#define AbstractBoard_h
#define EMPTY 0
#define COM -1
#define MAN 1
#define DRAW 0
#define STEP 9
#define ROW 3
#define COL 3
#define BUFFER_SIZE 1024
typedef struct _Move
{
int x ;
int y ;
char mesg[50] ;
_Move()
{
x = -1 ;
y = -1 ;
memset(mesg,0,sizeof(mesg));
}
_Move(int x,int y)
{
this->x = x ;
this->y = y ;
}
void setMessage(int state)
{
if(MAN == state)
{
memcpy(mesg, "you win!\n", sizeof(mesg));
std::cout<<"man win"<<std::endl;
}
else if(COM == state)
{
memcpy(mesg, "computer win!\n", sizeof(mesg));
std::cout<<"computer"<<std::endl;
}
else
{
memcpy(mesg, "draw !\n", sizeof(mesg));
std::cout<<"draw"<<std::endl;
}
}
}Move;
class AbstractBoard {
public:
AbstractBoard():currentDepth(9),step(1),manFirst(false)
{}
virtual~AbstractBoard(){}
int player ;
int currentDepth ;
int step ;
bool manFirst ;
int board[3][3] = {0};
virtual bool manPlay() = 0;
virtual bool comPlay() = 0;
void printBoard() {
int i, j;
if(player == COM)
{
std::cout<<"man plays:"<<std::endl;
}
else if(player == MAN)
{
std::cout<<"computer plays:"<<std::endl;
}
for (i = 0; i < COL; i++)
{
printf("-------------\n");
for (j = 0; j < ROW; j++)
{
if (board[i][j] == COM)
{
printf("| X ");
}
else if (board[i][j] == MAN)
{
printf("| O ");
}
else
{
printf("| ");
}
}
printf("|\n");
}
printf("-------------\n");
}
Move recvMove(int &clnt_sock)
{
Move result ;
char buffer[BUFFER_SIZE] = {0} ;
while (1) {
if(recv(clnt_sock, buffer, BUFFER_SIZE, 0))
{
break ;
}
}
memcpy(&result,buffer,sizeof(Move));
return result ;
}
void sendMove(const Move &m,int &clnt_sock)
{
char buffer[BUFFER_SIZE] = {0} ;
memcpy(buffer,&m,sizeof(Move));
send(clnt_sock,buffer,sizeof(Move),0);
}
};
#endif /* AbstractBoard_h */
2.Server
//
// Board.hpp
// TicTacToeServer
//
// Created by Tusko on 2017/4/9.
// Copyright © 2017年 Tusko. All rights reserved.
//
#ifndef Board_hpp
#define Board_hpp
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string>
#include "AbstractBoard.h"
#define MAX_NUM 1000;
class TTTServer :public AbstractBoard {
public:
TTTServer(){
ServSocket();
}
~TTTServer(){}
int tempBoard[3][3] = {0};
private:
int serv_sock ;
int clnt_sock ;
Move bestMove ;
void ServSocket()
{
serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in serv_addr ;
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(6666);
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(serv_sock, 20);
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size = sizeof(clnt_addr);
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
char str[] = "connect TTTserver successfully\n";
write(clnt_sock, str, sizeof(str));
char buffer[BUFFER_SIZE] = {0};
read(clnt_sock, buffer, sizeof(buffer) - 1);
printf("Message form TTTclient: %s\n", buffer);
setFirst();
startPlay();
}
void startPlay()
{
if(manFirst)
{
player = MAN;
while (1) {
if(!manPlay())
{
break ;
}
printBoard();
if (!comPlay())
{
break ;
}
printBoard();
}
printBoard();
}
else
{
player = COM;
while (1) {
if(!comPlay())
{
break ;
}
printBoard();
if (!manPlay())
{
break ;
}
printBoard();
}
printBoard();
}
sleep(2);
disconnect();
}
//set who plays first
void setFirst()
{
char buffer[BUFFER_SIZE];
read(clnt_sock, buffer, sizeof(buffer) - 1);
if(buffer[0] == 'm')
{
manFirst = true ;
}
}
int isWin() {
for (int i = 0; i < COL; i++)
{
if (board[i][0] + board[i][1] + board[i][2] == 3)
return 1;
else if (board[i][0] + board[i][1] + board[i][2] == -3)
return -1;
}
for (int j = 0; j < ROW; j++)
{
if (board[0][j] + board[1][j] + board[2][j] == 3)
return 1;
else if (board[0][j] + board[1][j] + board[2][j] == -3)
return -1;
}
if (board[0][0] + board[1][1] + board[2][2] == 3 || board[0][2] + board[1][1] + board[2][0] == 3)
return 1;
else if (board[0][0] + board[1][1] + board[2][2] == -3 || board[0][2] + board[1][1] + board[2][0] == -3)
return -1;
else return 0;
}
//评估函数
int evaluteMap() {
int i, j;
if (isWin() == COM)
{
return MAX_NUM;
}
if (isWin() == MAN)
{
return -MAX_NUM;
}
int count = 0;
for (i = 0; i < ROW; i++)
for (j = 0; j < COL; j++)
{
if (board[i][j] == EMPTY)
tempBoard[i][j] = COM;
else
tempBoard[i][j] = board[i][j];
}
for (i = 0; i < ROW; i++)
count += (tempBoard[i][0] + tempBoard[i][1] + tempBoard[i][2]) / 3;
for (i = 0; i < COL; i++)
count += (tempBoard[0][i] + tempBoard[1][i] + tempBoard[2][i]) / 3;
count += (tempBoard[0][0] + tempBoard[1][1] + tempBoard[2][2]) / 3;
count += (tempBoard[2][0] + tempBoard[1][1] + tempBoard[0][2]) / 3;
for (i = 0; i < ROW; i++)
for (j = 0; j < COL; j++)
{
if (board[i][j] == EMPTY)
tempBoard[i][j] = MAN;
else tempBoard[i][j] = board[i][j];
}
for (i = 0; i < 3; i++)
count += (tempBoard[i][0] + tempBoard[i][1] + tempBoard[i][2]) / 3;
for (i = 0; i < 3; i++)
count += (tempBoard[0][i] + tempBoard[1][i] + tempBoard[2][i]) / 3;
count += (tempBoard[0][0] + tempBoard[1][1] + tempBoard[2][2]) / 3;
count += (tempBoard[2][0] + tempBoard[1][1] + tempBoard[0][2]) / 3;
return count;
}
void makeMove(const Move &curMove)
{
board[curMove.x][curMove.y] = player;
player = (player == COM) ? MAN : COM;
}
void unMakeMove(Move curMove) {
board[curMove.x][curMove.y] = 0;
player = (player == COM) ? MAN : COM;
}
//get empty place
int getMoveList(Move moveList[]) {
int moveCount = 0;
int i, j;
for (i = 0; i < COL; i++)
{
for (j = 0; j < ROW; j++)
{
if (board[i][j] == 0)
{
moveList[moveCount].x = i;
moveList[moveCount].y = j;
moveCount++;
}
}
}
return moveCount;
}
int miniMaxsearch(int depth)
{
int value;
int bestValue = 0;
int moveCount = 0;
int i;
Move moveList[9];
if (isWin() == COM || isWin() == MAN)
{
return evaluteMap();
}
//if depth equal zero,return value
if (depth == 0)
{
return evaluteMap();
}
if (COM == player) {
bestValue = -MAX_NUM;
}
else if (MAN == player)
{
bestValue = MAX_NUM;
}
moveCount = getMoveList(moveList);
for (i = 0; i < moveCount; i++)
{
Move curMove = moveList[i];
makeMove(curMove);
value = miniMaxsearch(depth - 1);
unMakeMove(curMove);
if (player == COM)
{
if (value > bestValue)
{
bestValue = value;
if (depth == currentDepth)
{
bestMove = curMove;
}
}
}
else if (player == MAN)
{
if (value < bestValue)
{
bestValue = value;
if (depth == currentDepth)
{
bestMove = curMove;
}
}
}
}
return bestValue;
}
virtual bool manPlay()
{
Move move = recvMove(clnt_sock);
board[move.x][move.y] = MAN;
step++;
currentDepth--;
if (player == isWin()) {
bestMove.setMessage(MAN);
sendMove(bestMove, clnt_sock);
return false ;
}
else if(10 == step)
{
bestMove.setMessage(DRAW);
sendMove(bestMove, clnt_sock);
return false ;
}
player = (player == COM) ? MAN : COM;
return true ;
}
virtual bool comPlay()
{
miniMaxsearch(currentDepth);
board[bestMove.x][bestMove.y] = COM;
step++;
currentDepth--;
if (player == isWin()) {
bestMove.setMessage(COM);
sendMove(bestMove, clnt_sock);
return false ;
}
else if(10 == step)
{
bestMove.setMessage(DRAW);
sendMove(bestMove, clnt_sock);
return false;
}
sendMove(bestMove, clnt_sock);
player = (player == COM) ? MAN : COM;
return true ;
}
void disconnect()
{
close(serv_sock);
close(clnt_sock);
}
};
#endif /* Board_hpp */
Server main.cpp
//
// main.cpp
// TicTacToeServer
//
// Created by Tusko on 2017/4/9.
// Copyright © 2017年 Tusko. All rights reserved.
//
#include <iostream>
#include "Board.hpp"
int main(int argc, const char * argv[]) {
TTTServer TTTserv ;
return 0;
}
3.Client
//
// Board.hpp
// TicTacToeServer
//
// Created by Tusko on 2017/4/9.
// Copyright © 2017年 Tusko. All rights reserved.
//
#ifndef Board_hpp
#define Board_hpp
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
#include <string>
#include "AbstractBoard.h"
#define X -1
#define O 1
#define BUFFER_SIZE 1024
class TTTClient : public AbstractBoard{
public:
TTTClient(){
ClntSocket();
}
private:
int clnt_sock ;
void ClntSocket()
{
clnt_sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr;
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(6666);
connect(clnt_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
char buffer[BUFFER_SIZE] = {0};
while (1) {
if ( read(clnt_sock, buffer, sizeof(buffer) - 1) )
{
printf("Message form TTTserver: %s\n", buffer);
char str[] = "connect TTTClient successfully\n";
write(clnt_sock, str, sizeof(str));
break ;
}
}
std::cout<<"Who plays first? m - man(O) , c - computer(X)"<<std::endl ;
std::cout<<"Enter your select:"<<std::endl ;
char ch[20] = {0};
std::cin>>ch;
while ( !( ch[0] == 'c' || ch[0] == 'm') || strlen(ch) > 1) {
std::cout<<"Select wrong,enter again:"<<std::endl ;
std::cin>>ch;
}
if(ch[0] == 'm')
{
manFirst = true ;
}
write(clnt_sock,ch, sizeof(ch));
startPlay();
}
void startPlay()
{
printBoard();
if(manFirst)
{
player = MAN;
while (1)
{
manPlay();
if(!comPlay())
{
break ;
}
}
}
else
{
player = COM;
while (1) {
if(!comPlay())
{
break ;
}
manPlay();
}
}
sleep(3);
disconnect();
}
virtual bool manPlay()
{
Move move ;
int x, y;
printf("请输入位置坐标 e.g :(0 0)为左上角 (2,2)为右下角 \n");
scanf("%d", &x);
scanf("%d", &y);
while (x < 0 || x > 2 || y < 0 || y > 2)
{
printf("您输入的坐标错误,请重新输入:x:(0~2) , y:(0~2)\n");
scanf("%d", &x);
scanf("%d", &y);
}
while (board[x][y] != 0)
{
printf("该位置已有棋,请重新输入:\n");
scanf("%d", &x);
scanf("%d", &y);
}
move.x = x ;
move.y = y ;
board[move.x][move.y] = MAN;
sendMove(move, clnt_sock);
return true ;
}
virtual bool comPlay()
{
Move move = recvMove(clnt_sock);
if(strlen(move.mesg) > 0)
{
if(-1 !=move.x)
{
board[move.x][move.y] = COM;
printBoard();
}
std::cout<<move.mesg<<std::endl;
return false;
}
board[move.x][move.y] = COM;
printBoard();
return true ;
}
void disconnect()
{
close(clnt_sock);
}
};
#endif /* Board_hpp */
Client main.cpp
//
// main.cpp
// TicTacToe_Client
//
// Created by Tusko on 2017/4/9.
// Copyright © 2017年 Tusko. All rights reserved.
//
#include <iostream>
#include "Board.hpp"
int main(int argc, const char * argv[]) {
TTTClient TTTclnt ;
return 0;
}