Java swing + socket 寫的一個五子棋網路對戰遊戲
##網路對戰版本的五子棋遊戲,包含服務端和客戶端(c/s模式) ,寫成此文章給需要或想研究的人一些參考(相關程式碼我會放在本文的最後)
遊戲效果圖:
###下載線上客戶端版本試玩:
(插播:下面連結下載的客戶端如果登入不了伺服器的話,請直接下載文章最後的程式碼到本地除錯,因為伺服器不是我的,只是借用的別人的,別人不開心給我X掉了我也沒辦法,但程式碼已經經過不下十個人測試了,是OK的,放心下載!)
連結:https://pan.baidu.com/s/1-Bt8tcuZGkVj-jFxN4YDbg 密碼:w2oy
要求:jdk環境 1.6或以上
使用方式:環境正常安裝後,解壓下載的檔案,點選 startClient.bat 就可以打開了
備註:若你只開一個客戶端,進行匹配的話可能沒人跟你玩,建議不是為了技術純測試的話與你和你的朋友一起對戰
說明:伺服器部署在騰訊雲上面,但是租期只有一年,客戶端依賴伺服器,若您下載的客戶端打不開了請下載文章最後面的程式碼在本地進行研究
閱讀本文前,您需要了解:
- java swing(好像是廢話)
- socket
- json
- 多執行緒(不高,倒計時用了一下)
##1:服務端與客戶端資料互動如何約定?
#####在c/s程式的設計之初,如何按照約定的方式進行資料互動一直是一個需要解決的問題,在我這個程式中,有一個常量類定義如下,這是我與客戶端進行的一個約定,任何請求都會含有一個基礎資料(key),而所做的工作就是這個基礎資料(key)所對應的基礎資料(value)
package cn.xt.net; import java.text.SimpleDateFormat; import java.util.Date; import java.util.UUID; public class Const { public static final int PORT = 7001; // 監聽埠 // 基礎資料(key) public static final String ID = "id"; public static final String MSG = "msg"; // 基礎資料(value) public static final String ID_STATUS_ERROR = "idError"; // 錯誤資訊 public static final String ID_STATUS_INIT = "初始化客戶端"; // 向伺服器請求初始化 public static final String ID_STATUS_PP = "匹配玩家"; public static final String PP_SUCCESS = "匹配成功"; public static final String ID_STATUS_PUT = "傳送落子位置"; public static final String ID_STATUS_GET = "獲取落子位置"; public static final String ID_STATUS_OVER = "對局結束"; public static final String ID_STATUS_MSG = "聊天訊息"; public static final String ID_STATUS_BACK = "請求悔棋"; public static final String ID_STATUS_FAIL = "認輸"; public static final String ID_STATUS_HANDSNAKE = "初次握手"; public static final String ID_STATUS_BACK_RESULT = "請求悔棋結果"; public static final String ID_STATUS_OVERTIME = "遊戲超時"; public static final String SIZE = "棋盤長度"; public static final String EXISTS = "該使用者名稱已存在系統中"; public static final String USER_NAME = "userName"; public static final String INIT_SUCCESS = "初始化成功"; public static final String X = "x"; public static final String Y = "y"; public static final String STATUS = "status"; // 當前棋子的狀態 public static final String COLOR = "落子顏色"; public static final String SYSTEM_MSG = "系統訊息"; // key - value public static final String MY = "my"; // 玩家 public static final String YOU = "you"; // 對家 public static final String FIRST = "先手方"; // 1:先手; 0:後手 // 屬於頁面的專屬資料 private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); public static String getId() { return getSysDate() + "-" + UUID.randomUUID().toString(); } public static String getSysDate(){ return sdf.format(new Date()); } // public static void main(String[] args) { // System.out.println(Const.getId()); // } }
##2:服務端構建
#####客戶端的連線資訊將會在伺服器定義一個Map儲存,該實體類定義如下:
儲存客戶端所有資訊的Map: private static volatile Map<String, UserData> client = new HashMap<>(); 實體類定義(類名:UserData): private MessageFormat msgFormat; // 處理訊息的物件 private Socket socket; // 客戶端套接字物件 private Thread thread; // 處理訊息的執行緒 private String userId; // 登陸的使用者(唯一性) private String userName; // 客戶的名字(自己取的名稱) private String status; // 狀態(0:登陸; 1:準備; 2:對局開始; 3:觀戰) private String enemy; // 對家 private String isFirst; // 是否為先手 private List<Chess> chessBoard; // 棋盤 private boolean isOver; // 對局是否結束
#####定義一個普通的啟動類,繼承Thread,重寫Thread類中的run方法以監聽客戶端socket請求,其中MessageFormat類是處理客戶端的傳送/傳輸類,每個客戶端連線後都會生成一個inputstream與outputstream(I/O)流,MessageFormat對其進行了相關的封裝,也是統一處理客戶端傳送的資訊和傳送資訊給客戶端,在下面我會重點介紹該類,該類也是處理訊息的一個核心類。
private ServerSocket ss;
private Socket s;
private boolean start = false;
private static volatile Map<String, UserData> client = new HashMap<>();
public static void main(String[] args) {
SuperServlet servlet = new SuperServlet();
servlet.startService();
}
public SuperServlet() {
try {
ss = new ServerSocket(Const.PORT);
} catch (IOException e) {
e.printStackTrace();
}
}
// 執行緒用來接收客戶端連線的請求
@Override
public void run() {
try {
while (start) {
s = ss.accept(); // 一直監聽客戶端的請求
synchronized (s) {
String userId = Const.getId(); // 返回一個唯一id
MessageFormat cs = new MessageFormat(s); // 建立傳輸入資料的執行緒(服務類)
// 為每個連線的客戶端定義一個執行緒為其服務
Thread th = new Thread(cs);
// 定義使用者資訊類
UserData ud = new UserData();
ud.setUserId(userId);
ud.setMsgFormat(cs);
ud.setSocket(s);
ud.setThread(th);
client.put(userId, ud); // 將這個使用者儲存到伺服器中
th.start(); // 啟動這個執行緒
// 將識別ID發給客戶端儲存
JSONObject responseMsg = new JSONObject();
responseMsg.put(Const.ID, Const.ID_STATUS_HANDSNAKE);
responseMsg.put(Const.MSG, userId);
cs.send(responseMsg); // 傳送訊息給客戶端
System.out.println("一個客戶連線... 線上數量:" + client.size());
}
}
} catch (Exception er) {
System.out.println("服務已啟動或埠被佔用!");
er.printStackTrace();
System.exit(0);
}
}
##2.1:伺服器之MessageFormat類
#####前面我講到,該類處理客戶端的請求與服務端傳送給該客戶端的請求,為不給伺服器造成阻塞,該類肯定是一個執行緒,簡單的看一下該類定義的幾個方法:
// 預設構造器,對新連線的客戶端的I/O流進行封裝
public MessageFormat(Socket s)
// 重寫該執行緒的run方法,用於迴圈監聽客戶端的傳送的資訊
public void run()
// 傳送資訊給該客戶端
public void send(final JSONObject msg)
// 該客戶端主動傳送資訊給別的客戶端
public void send(String userId, JSONObject msg)
// 客戶端斷開時關閉I/O流
public void close()
// 訊息處理中心(msg為客戶端發過來的json資料)
public void addInfo(String msg)
#####服務端重點處理的是客戶端發過來的資訊,所以先來講解處理的方式:
對著下面的程式碼,我們先是迴圈監聽客戶端發過來的資訊,dis.readUTF()是一個阻塞式的方法,若客戶端發過來資訊則返回一個字串,接著在這裡只是進行簡單的列印就將資訊傳送給了addInfo() 這個方法;在我的catch中進行了相關處理,這裡主要處理客戶端若遇到不可描述的事情斷開後,伺服器應主動踢出這個客戶端,這裡我是先遍歷了伺服器中所有的客戶端,找到一個執行緒ID與之匹配的,然後把這個執行緒踢出Map
@Override
public void run() {
while (clientStart) {
try {
// 客戶端傳來的資訊
String requestMsg = dis.readUTF();
System.out.println("service do in:" + requestMsg);
// LogUtils.write("service do in:" + requestMsg);
// 訊息處理
addInfo(requestMsg);
} catch (IOException e) {
// 如果無法接收客戶端的資訊
for(Entry<String, UserData> set : SuperServlet.getClient().entrySet()){
String userId = set.getKey();
UserData ud = set.getValue();
// 匹配退出的客戶端程序
if(this == ud.getMsgFormat()){
// 給對手傳送退出資訊
JSONObject responseMsg = new JSONObject();
responseMsg.put(Const.ID, Const.ID_STATUS_FAIL);
UserData clientData = SuperServlet.getClient().get(ud.getEnemy());
if(clientData != null && !clientData.isOver()){
clientData.getMsgFormat().send(responseMsg);
}
MessageFormatHelper.ppList.remove(ud.getUserId());
System.out.println("退出的客戶端為:"+ ud.getUserName() + " --> " + ud.getUserId());
SuperServlet.getClient().remove(userId); // 伺服器移除這個使用者
System.out.println("線上數量:" + SuperServlet.getClient().size());
// ud.getThread().interrupt(); // 等待執行緒關閉
close(); // 關閉相應的流
clientStart = false; // 預先停止 while迴圈
ud.getThread().stop(); // 等待執行緒關閉
}
}
}
}
}
#####由上面的程式碼可以看到,客戶端的所有資訊我是將由addInfo()方法處理的:
對著下面程式碼,我們先是將傳過來的資訊進行轉義替換,然後轉換成JSON,取到開始時常量類裡面定義的key,進行switch匹配,匹配到對應的ID就做對應的事,這種做法在SpringMVC中跟RequestMapper有著異曲同工之想法。在這裡,有一處地方我沒有做講解,MessageFormatHelper這個類是做什麼的呢?在我們寫Javaweb專案時,常會寫一個dao 與 daoImpl,在這裡MessageFormatHelper相當於 daoImpl類,也就是實現具體功能的類。
public void addInfo(String msg) {
JSONObject json = JSONObject.fromObject(msg.replaceAll("\"", "\\\""));
MessageFormatHelper.initialized(json); // 優先初始化伺服器資料
String id = json.get(Const.ID).toString();
switch (id) {
// 初始化
case Const.ID_STATUS_INIT:
MessageFormatHelper.init();
break;
// 匹配玩家
case Const.ID_STATUS_PP:
MessageFormatHelper.matchUser();
break;
// 落子
case Const.ID_STATUS_PUT:
MessageFormatHelper.putChess();
break;
// 獲取棋子
// case Const.ID_STATUS_GET:
// MessageFormatHelper.getChess();
// break;
// 對局結束
case Const.ID_STATUS_OVER:
MessageFormatHelper.gameOver();
break;
// 悔棋請求
case Const.ID_STATUS_BACK:
MessageFormatHelper.toBack();
break;
// 悔棋請求的結果
case Const.ID_STATUS_BACK_RESULT:
MessageFormatHelper.toBackResult();
break;
// 認輸
case Const.ID_STATUS_FAIL:
MessageFormatHelper.fail();
break;
// 聊天訊息
case Const.ID_STATUS_MSG:
MessageFormatHelper.chatMsg();
break;
// 遊戲超時
case Const.ID_STATUS_OVERTIME:
MessageFormatHelper.overTime();
break;
default:
System.out.println("server: 未匹配到分支; id:" + id);
break;
}
// 未找到匹配分支
// JSONObject result = new JSONObject();
// result.put(Const.ID, "未找到匹配分支");
// return result;
}
#####MessageFormatHelper類實現的功能在MessageFormat中皆有體現並有註釋,因程式碼太多,大家自行下載程式碼研究,我的程式碼註釋一般都寫的比較詳細,且思路通俗易懂;這裡貼幾個少一點的功能程式碼
/**
* 雙方棋子互傳
*/
public static void putChess() {
// 設定我方棋盤
String userId = json.getString(Const.MY);
// 獲取座標
int x = json.getInt(Const.X);
int y = json.getInt(Const.Y);
String color = json.getString(Const.COLOR);
// 更新我方棋盤
UserData my = SuperServlet.getClient().get(userId);
my.getChessBoard().add(new Chess(x, y, color));
// 更新對方棋盤
UserData you = SuperServlet.getClient().get(my.getEnemy());
you.getChessBoard().add(new Chess(x, y, color));
// 更新伺服器資料
SuperServlet.updateClient(userId, my);
SuperServlet.updateClient(you.getUserId(), you);
// 將棋子同步給對手
if(!you.isOver()){
json.put(Const.ID, Const.ID_STATUS_GET);
you.getMsgFormat().send(json);
} else {
System.out.println("無將棋子同步給對手, 對方已結束遊戲");
}
}
/**
* 聊天訊息處理
*/
public static void chatMsg() {
result.put(Const.ID, Const.ID_STATUS_MSG);
String userId = json.getString(Const.MY);
UserData my = SuperServlet.getClient().get(userId);
// 獲取對手名稱
UserData you = SuperServlet.getClient().get(my.getEnemy());
if(you != null){
result.put(Const.MSG, json.getString(Const.MSG));
result.put(Const.MY, my.getUserName());
you.getMsgFormat().send(result);
}
}
/**
* 悔棋請求
*/
public static void toBack(){
String userId = json.getString(Const.MY);
UserData my = SuperServlet.getClient().get(userId);
result.put(Const.ID, Const.ID_STATUS_BACK);
UserData you = SuperServlet.getClient().get(my.getEnemy());
you.getMsgFormat().send(result);
}
/**
* 悔棋結果
*/
public static void toBackResult(){
String userId = json.getString(Const.MY);
UserData my = SuperServlet.getClient().get(userId);
result.put(Const.ID, Const.ID_STATUS_BACK_RESULT);
result.put(Const.MSG, json.getString(Const.MSG));
UserData you = SuperServlet.getClient().get(my.getEnemy());
you.getMsgFormat().send(result);
// 更新伺服器棋盤以備觀戰
if("同意".equals(json.getString(Const.MSG))){
my.getChessBoard().remove(my.getChessBoard().size() - 1);
you.getChessBoard().remove(you.getChessBoard().size() - 1);
SuperServlet.updateClient(my.getUserId(), my);
SuperServlet.updateClient(you.getUserId(), you);
}
}
##3:客戶端
#####客戶端傳送資訊與伺服器互動採用的方式跟伺服器處理資訊的方式是一樣的,所以沒有什麼可講的,主要講一下棋盤的繪製與如何判斷已經勝利。
##3.1:客戶端之JPanel
#####在這個之前還有一個JFrame,學過awt的應該知道 frame,而JFrame則是swing,swing是AWT的之類,相關資料可以查詢API,JPanel是一個容器,而JFrame是一個視窗,這裡直接跳過JFrame講JPanel,因為繪製棋盤的甩有功能都是在這個容器裡面實現的
/**
* 相關屬性定義
*/
private int span = 35; // 棋盤格子寬度
private int margin = 22;
private final int DIAMETER = 35; // 直徑
private final int row = 15; // 棋盤行、列
private final int col = 15;
private int i = 0;
private boolean isBlack = true;
private boolean isPicture = true;// 是否用圖片作為背景(圖片是正常遊戲背景,false為測試遊戲背景)
private ImageIcon img = new ImageIcon("src/images/board.jpg");
private List<Chess> list = new LinkedList<Chess>(); // 整個棋盤
public boolean gameOver = true; // 預設結束遊戲
// 網路資料
public boolean isNetworkPK = false;
public boolean myChessColor = false; // 記錄我方落子的顏色
public boolean failFlag = false; // 認輸標記
public MessageQueuePanel MQPanel;
public String userName = null; // 我的名稱
public String userId = null; // 我的ID
以上定義中,棋盤就是我們的List,重寫 JPanel 的Paint方法繪製遊戲棋盤
@Override
public void paint(Graphics g) {
super.paint(g);
// span = this.getHeight() / row; // 當視窗被拖動,動態重新整理視窗
Graphics2D g2 = (Graphics2D) g;
// 正常遊戲繪製的棋盤是一張背景圖片
g.drawImage(img.getImage(), 1, 0, null);
RadialGradientPaint rgp = null;
// 畫棋子
for (i = 0; i < list.size(); ++i) {
Chess chess = list.get(i);
int xPos = chess.getX() * span + margin; // 將真實座標轉換成網格座標
int yPos = chess.getY() * span + margin;
g2.setColor(chess.getColors()); // 設定畫筆顏色
if (chess.getColors() == Color.BLACK) {
rgp = new RadialGradientPaint(xPos - DIAMETER / 2 + 26, //
yPos - DIAMETER / 2 + 12, 20, new float[] { 0.0f, 1.0f }, //
new Color[] { Color.WHITE, Color.BLACK });
g2.setPaint(rgp);
} else {
// x, y, 直徑, 漸變度, 漸變色
rgp = new RadialGradientPaint(xPos - DIAMETER / 2 + 25, //
yPos - DIAMETER / 2 - 30, 60, new float[] { 0f, 1f }, //
new Color[] { Color.BLACK, Color.WHITE });
g2.setPaint(rgp);
}
// 去鋸齒
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT);
g2.fillOval(xPos - DIAMETER / 2, yPos - DIAMETER / 2, span - 1, span);
// 畫紅色矩形
g2.setColor(Color.RED);
if (i == list.size() - 1)
g2.draw3DRect(xPos - DIAMETER / 2 - 1, //
yPos - DIAMETER / 2 - 1, span, span + 1, true);
}
}
##3.2:客戶端之如何判斷勝利
#####在isWin方法中,我們傳入整個棋盤,以及當前的落子位置和當前落子的顏色,五子棋判斷勝利相關於一個‘米’字,實則只需要掃描一條邊後,然後判斷一下同顏色的連子數是否大於等於五個就可判斷它是否已經勝利了,在程式碼中前兩個迴圈是判斷第一條橫線,分別判斷從落子位置的左邊和右邊,也就是米字中的 一 ,其它判斷通俗易懂,大家可自行領悟
// 判斷勝利的方法
private boolean isWin(List<Chess> list, int xPos, int yPos, boolean isBlack) {
int chessCount = 1;
final int max = 5; // 連子數
int x = 0, y = 0;
Color color = (isBlack ? Color.BLACK : Color.WHITE);
// 當前位置向左
for (x = xPos - 1; x >= 0; --x) {
if (getChess(list, x, yPos, color) != null)
chessCount++;
else
break;
}
// 當前位置向右
for (x = xPos + 1; x <= row; ++x) {
if (getChess(list, x, yPos, color) != null)
chessCount++;
else
break;
}
if (chessCount >= max)
return true;
else
chessCount = 1;
// 當前位置向上
for (y = yPos - 1; y >= 0; --y) {
if (getChess(list, xPos, y, color) != null)
chessCount++;
else
break;
}
// 當前位置向下
for (y = yPos + 1; y <= col; ++y) {
if (getChess(list, xPos, y, color) != null)
chessCount++;
else
break;
}
if (chessCount >= max)
return true;
else
chessCount = 1;
// 左斜著向上
for (x = xPos - 1, y = yPos - 1; x >= 0 && y >= 0; --x, --y) {
if (getChess(list, x, y, color) != null)
chessCount++;
else
break;
}
// 右斜著向下
for (x = xPos + 1, y = yPos + 1; x <= row && y <= col; ++x, ++y) {
if (getChess(list, x, y, color) != null)
chessCount++;
else
break;
}
if (chessCount >= max)
return true;
else
chessCount = 1;
// 右斜著向上
for (x = xPos + 1, y = yPos - 1; x <= row && y >= 0; ++x, --y) {
if (getChess(list, x, y, color) != null)
chessCount++;
else
break;
}
// 左斜著向下
for (x = xPos - 1, y = yPos + 1; x >= 0 && y <= col; --x, ++y) {
if (getChess(list, x, y, color) != null)
chessCount++;
else
break;
}
if (chessCount >= max)
return true;
else
chessCount = 1;
return false;
}
相關推薦
Java swing + socket 寫的一個五子棋網路對戰遊戲
##網路對戰版本的五子棋遊戲,包含服務端和客戶端(c/s模式) ,寫成此文章給需要或想研究的人一些參考(相關程式碼我會放在本文的最後) 遊戲效果圖: ###下載線上客戶端版本試玩: (插播:下面連結下載的客戶端如果登入不了伺服器的話,請直接下載文章最後的程式碼
java多執行緒系列:通過對戰遊戲學習CyclicBarrier
CyclicBarrier是java.util.concurrent包下面的一個工具類,字面意思是可迴圈使用(Cyclic)的屏障(Barrier),通過它可以實現讓一組執行緒到達一個屏障(也可以叫同步點)時被阻塞,直到最後一個執行緒到達屏障時,所有被屏障攔截的執
UE4 多人網路對戰遊戲筆記
1.給物體施加一個徑向力 定義一個徑向力: URadialForceComponent* RadialForceComp; 在建構函式裡賦預設值: RadialForceComp = CreateDefaultSubobject<URadialForceComponent>(TEXT("R
websocket入門(2)——使用socket.io實現網路對戰版五子棋
五子棋網路對戰版說明 1、安裝與執行 請完整的down下除了node_modules資料夾以外的所有檔案,然後在控制檯執行 npm install來進行安裝。 安裝完畢後,通過 npm start 命令來執行伺服器,然後通過 127.0.0.1
用java類和物件寫一個簡單的回合制對戰遊戲
一、什麼是物件,什麼是類類比現實生活,任何一個具體的事物都可以稱為物件,比如某臺電腦,某本書,某個人……而類就是根據物件相似的特徵和功能進行分類,物件是具體的,類是抽象的。二、類的基本格式public class 類名 {//定義屬性訪問修飾符 資料型別 屬性名;//定義
Java編程寫一個會導致死鎖的程序
font gpo bject clas 代碼塊 style 相互 class 同步 線程A和線程B相互等待對方持有的鎖導致程序無限死循環下去。真正理解什麽是死鎖,這個問題其實不難,幾個步驟:(1)兩個線程裏面分別持有兩個Object對象:lock1和lock2。這兩個loc
用socket寫一個簡單的聊天程式
服務端程式碼: # 建立socket物件:socket.socket()——>繫結IP地址和埠:bind——>監聽:listen——># 得到請求:accept——>接收請求:recv——>傳送資訊:send——>關閉close i
用socket寫一個簡單的聊天程序
cli list t對象 bind bre hid lis 結束 聊天 服務端代碼: # 創建socket對象:socket.socket()——>綁定IP地址和端口:bind——>監聽:listen——># 得到請求:accept——>接收請求:r
用Java語言如何寫一個小日曆
利用GregorianCalendar物件內部的get方法 getFirstDayweek方法獲得當地星期的起始日。 我們不必知道GregorianCalendar類如何計算星期數與天數。掌握set與get,add方法。 在這裡插入程式碼片 ```package
什麼是java序列化,如何實現java序列化?(寫一個例項)?
原文地址:http://blog.csdn.net/cselmu9/article/details/41908741 Java 序列化技術可以使你將一個物件的狀態寫入一個Byte 流裡,並且可以從其它地方把該Byte 流裡的資料讀出來,重新構造一個相同的物件。這種機制允
Java練習題_寫一個函式reverseList,該函式能夠接受一個List,然後把該List 倒序排列
(List)寫一個函式reverseList, 該函式能夠接受一個List, 然後把該List 倒序排列。 例如: List list = new ArrayList(); list.add(“Hello”); list.add(“World”); list.add(“Learn”); //此時l
Java程式設計題: 寫一個Singleton出來
Singleton模式主要作用是保證在Java應用程式中,一個類Class只有一個例項存在。 一般Singleton模式通常有幾種種形式: 第一種形式: 定義一個類,它的建構函式為private的,它有一個static的private的該類變數,在類初始化時例項話,通過一個p
java五子棋人機對戰演算法分析
五子棋人機對戰採用的演算法,目前大都是搜尋樹演算法,用一棵樹來表示棋局發展的 種種可能性,這種樹叫做博弈樹(對局樹)。根節點表示對局的開始狀態,每一種可 能的走法造成的結果作為其子節點,而對每一個這樣的子節點,考慮另一方的各種 可能應對,作為下一層的子節點,這
學習java Swing程式設計的一個簡單Demo
import java.lang.*; import java.awt.*; import javax.swing.*; import java.awt.event.*; public class SwingComponent { //建立窗體,JFrame J
使用socket寫一個簡單的聊天程式&碰到的問題
程式分成2個部分:1,服務端,用來接受客戶端發來的資訊2,客戶端,用來向服務端發信息。一、服務端如下:SocketServerThread.java用來處理客戶端傳送的資訊package com.thread.socket.service; import java.io.B
深入Java日記——自己寫一個ORM框架(1)
眾所周知,ORM框架有很多,例如Hibernate,MyBatis,還有BeetlSQL等等,裡面獲取有很多我們不需要的功能,本系列部落格主要教大家如何寫一個簡單的ORM框架 這個ORM框架主要有以下功能: 1. 生成JavaBean程式碼 2. 通過
c語言實現五子棋人人對戰
利用簡單的c語言基礎 實現最簡單的功能 介面比較醜陋主要是剛學完c的一個小實踐 未使用MFC所以介面沒有很好看 主要目的加強對c語言的理解與運用 同時增加自己的程式碼量 首先要學一些標頭檔案可以看我的部落格前面的文章 要用到到的標頭檔案stdio.h stdlib
用C語言寫一個簡單的掃雷小遊戲
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <windows.h> #include <time.h> /* 用 C 語言寫一個簡單的掃雷遊戲 */ // 1.寫一個遊戲選單 M
websocket實現五子棋聯機對戰
GoBang.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <st
如何寫一個簡單的猜數字遊戲?60行程式碼搞定,進來轉轉吧
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> void menu() { printf("******************************\n"); printf(“歡迎來到猜數