first commit

main
dai_48k 2024-01-20 15:33:00 +08:00
commit 5fb38f50ed
16 changed files with 707 additions and 0 deletions

3
.gitignore vendored 100644
View File

@ -0,0 +1,3 @@
out/
.idea/
*.iml

View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: top.dreamcenter.go.client.Client

View File

@ -0,0 +1,78 @@
package top.dreamcenter.go.client;
import top.dreamcenter.go.msg.Cross;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 1919);
ObjectInputStream oi = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream os = new ObjectOutputStream(socket.getOutputStream());
new MainFrame(oi, os);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void sendMsg(ObjectOutputStream os, Object obj) {
try {
os.writeObject(obj);
} catch (IOException e) {
e.printStackTrace();
}
}
public static Object recMsg(ObjectInputStream oi){
try {
return oi.readObject();
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
return null;
}
}
public static void printMap(Cross[][] map, Cross now){
System.out.println();
int width = map.length - 1;
System.out.println("O A B C D E F G H I");
for (int i = 0; i < map.length; i++) {
System.out.print(i + 1 + " ");
for (int j = 0; j < map.length; j++) {
if (map[i][j] == Cross.NONE){
String tmp = "┼-";
if (i == 0) tmp = "┬-";
if (i == width) tmp = "┴-";
if (j == 0) tmp = "├-";
if (j == width) tmp = "┤ ";
if (i == 0 && j == 0) tmp = "┌-";
if (i == 0 && j == width) tmp = "┐";
if (i == width && j == 0) tmp = "└-";
if (i == width && j == width) tmp = "┘";
System.out.print(tmp);
} else if (map[i][j] == Cross.BLACK){
System.out.print("● ");
} else {
System.out.print("○ ");
}
}
System.out.println();
}
System.out.print((now == Cross.BLACK ? " [ ● ] " : " [ ○ ] ") + " : ");
}
}

View File

@ -0,0 +1,37 @@
package top.dreamcenter.go.client;
import top.dreamcenter.go.msg.Cross;
public class DataUtil {
public static String parseData(Cross[][] map){
StringBuilder sb = new StringBuilder();
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map.length; j++) {
sb.append(i).append(j);
if (map[i][j] == Cross.NONE) {
sb.append(0);
} else if (map[i][j] == Cross.BLACK) {
sb.append(1);
} else {
sb.append(2);
}
}
}
return sb.toString();
}
public static Cross[][] rawData(String data){
Cross[][] cross = new Cross[9][9];
Cross[] colors = new Cross[]{Cross.NONE, Cross.BLACK, Cross.WHITE};
for (int i = 0; i < data.length(); ) {
int row = data.charAt(i) - '0';
int col = data.charAt(i + 1) - '0';
Cross color = colors[data.charAt(i + 2) - '0'];
cross[row][col] = color;
i += 3;
}
return cross;
}
}

View File

@ -0,0 +1,145 @@
package top.dreamcenter.go.client;
import top.dreamcenter.go.msg.Cross;
import top.dreamcenter.go.msg.NewClick;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class MainFrame extends JFrame {
private static final int BASE_HEIGHT = 26;
private static final int BASE_FROM = 20;
Cross[][] map = new Cross[9][9];
Cross now = Cross.NONE;
public MainFrame(ObjectInputStream oi, ObjectOutputStream os) {
now = (Cross) Client.recMsg(oi);
System.out.println(now);
setTitle(now == Cross.BLACK ? "●" : "○");
setSize(200,200 + BASE_HEIGHT);
addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
int x = e.getX() - BASE_FROM;
int y = e.getY() - BASE_FROM - BASE_HEIGHT;
int col = (x%BASE_FROM < BASE_FROM / 2) ? (x / BASE_FROM) : (x / BASE_FROM + 1);
int row = (y%BASE_FROM < BASE_FROM / 2) ? (y / BASE_FROM) : (y / BASE_FROM + 1);
System.out.printf("[click] row: %d; col: %d;\n", row, col);
NewClick newClick = NewClick.newInstance(now, row + 1, col + 1);
Client.sendMsg(os, newClick);
repaint();
}
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
new Thread(() -> {
while (true) {
try {
String o = (String) Client.recMsg(oi);
map = DataUtil.rawData(o);
repaint();
} catch (Exception e){
e.printStackTrace();
System.out.println("连接断开");
break;
}
}
}).start();
}
@Override
public void paint(Graphics g) {
g.setColor(Color.white);
g.fillRect(0,BASE_HEIGHT,200,200);
g.setColor(Color.lightGray);
for (int i = 1; i <= 9; i++) {
drawLine(g, BASE_FROM, BASE_FROM * i, BASE_FROM * 9, BASE_FROM * i);
drawLine(g, BASE_FROM * i, BASE_FROM, BASE_FROM * i, BASE_FROM * 9);
}
g.setColor(Color.black);
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map.length; j++) {
if (map[i][j] != Cross.NONE) {
drawDot(g, i+1, j+1, map[i][j]);
}
}
}
}
@Override
public void update(Graphics g) {
Image tmp = createImage(200, 200);
Graphics tmpG = tmp.getGraphics();
paint(tmpG);
g.drawImage(tmp, 0,BASE_HEIGHT,null);
}
/**
* 线
* @param g g
* @param x1 x1
* @param y1 y1
* @param x2 x2
* @param y2 y2
*/
private void drawLine(Graphics g, int x1, int y1, int x2, int y2) {
g.drawLine(x1, BASE_HEIGHT + y1, x2, BASE_HEIGHT + y2);
}
/**
*
* @param g g
* @param cx x
* @param cy y
* @param r
*/
private void fillOval(Graphics g, int cx, int cy, int r) {
g.fillOval(cx - r, cy - r, r<<1, r<<1);
}
/**
*
* @param g g
* @param cx x
* @param cy y
* @param r
*/
private void drawOval(Graphics g, int cx, int cy, int r) {
g.drawOval(cx - r, cy - r, r<<1, r<<1);
}
/**
*
* @param g g
* @param row
* @param col
*/
private void drawDot(Graphics g, int row, int col, Cross cross){
if (cross == Cross.BLACK) {
fillOval(g, col * BASE_FROM , row * BASE_FROM + BASE_HEIGHT, BASE_FROM / 2);
}
if (cross == Cross.WHITE) {
drawOval(g, col * BASE_FROM , row * BASE_FROM + BASE_HEIGHT, BASE_FROM / 2 - 1);
drawOval(g, col * BASE_FROM , row * BASE_FROM + BASE_HEIGHT, BASE_FROM / 2 - 2);
}
}
}

View File

@ -0,0 +1,5 @@
package top.dreamcenter.go.msg;
public enum Cross {
NONE, BLACK, WHITE
}

View File

@ -0,0 +1,16 @@
package top.dreamcenter.go.msg;
import java.io.Serializable;
public class KVPair implements Serializable {
private static final long serialVersionUID = 19198686;
public int k;
public int v;
public KVPair(int k, int v) {
this.k = k;
this.v = v;
}
}

View File

@ -0,0 +1,20 @@
package top.dreamcenter.go.msg;
import java.io.Serializable;
public class NewClick implements Serializable {
private static final long serialVersionUID = 19196868;
public Cross color;
public KVPair pos;
private NewClick(Cross color, int row, int col) {
this.color = color;
pos = new KVPair(row, col);
}
public static NewClick newInstance(Cross color, int row, int col) {
return new NewClick(color, row, col);
}
}

View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: top.dreamcenter.go.server.Server

View File

@ -0,0 +1,5 @@
package top.dreamcenter.go.msg;
public enum Cross {
NONE, BLACK, WHITE
}

View File

@ -0,0 +1,24 @@
package top.dreamcenter.go.msg;
import java.io.Serializable;
public class KVPair implements Serializable {
private static final long serialVersionUID = 19198686;
public int k;
public int v;
public KVPair(int k, int v) {
this.k = k;
this.v = v;
}
@Override
public String toString() {
return "KVPair{" +
"k=" + k +
", v=" + v +
'}';
}
}

View File

@ -0,0 +1,29 @@
package top.dreamcenter.go.msg;
import java.io.Serializable;
public class NewClick implements Serializable {
private static final long serialVersionUID = 19196868;
public Cross color;
public KVPair pos;
private NewClick(Cross color, int row, int col) {
this.color = color;
pos = new KVPair(row, col);
}
public static NewClick newInstance(Cross color, int row, int col) {
return new NewClick(color, row, col);
}
@Override
public String toString() {
return "NewClick{" +
"color=" + color +
", pos=" + pos +
'}';
}
}

View File

@ -0,0 +1,155 @@
package top.dreamcenter.go.server;
import top.dreamcenter.go.msg.Cross;
import top.dreamcenter.go.msg.KVPair;
import top.dreamcenter.go.msg.NewClick;
import java.util.ArrayList;
import java.util.List;
public class CalDataUtil {
public static Cross[][] getInitMap() {
Cross[][] map = new Cross[9][9];
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map.length; j++) {
map[i][j] = Cross.NONE;
}
}
return map;
}
public static boolean draw(NewClick newClick, Cross[][] map) {
Cross cross = newClick.color;
int row = newClick.pos.k;
int col = newClick.pos.v;
return draw(cross, row, col, map);
}
public static boolean draw(Cross cross, int row, int col, Cross[][] map) {
int width = map.length;
if (row > width || row < 1 || col > width || col < 1 || (map[row - 1][col - 1] != Cross.NONE && cross != Cross.NONE)) return false;
map[row - 1][col - 1] = cross;
if (cross != Cross.NONE) check(cross, map);
return true;
}
public static void printMap(Cross[][] map, Cross now){
System.out.println();
int width = map.length - 1;
System.out.println("O A B C D E F G H I");
for (int i = 0; i < map.length; i++) {
System.out.print(i + 1 + " ");
for (int j = 0; j < map.length; j++) {
if (map[i][j] == Cross.NONE){
String tmp = "┼-";
if (i == 0) tmp = "┬-";
if (i == width) tmp = "┴-";
if (j == 0) tmp = "├-";
if (j == width) tmp = "┤ ";
if (i == 0 && j == 0) tmp = "┌-";
if (i == 0 && j == width) tmp = "┐";
if (i == width && j == 0) tmp = "└-";
if (i == width && j == width) tmp = "┘";
System.out.print(tmp);
} else if (map[i][j] == Cross.BLACK){
System.out.print("● ");
} else {
System.out.print("○ ");
}
}
System.out.println();
}
System.out.print((now == Cross.BLACK ? " [ ● ] " : " [ ○ ] ") + " : ");
}
/**
* clear dead : BFS
* @param cross cur
* @param map map
*/
private static void check (Cross cross, Cross[][] map) {
int width = map.length - 1;
// 遍历 黑色出现黑色时查找边缘只要存在存在空白则安全。bfs查找
Cross obc = cross == Cross.BLACK ? Cross.WHITE : Cross.BLACK;
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map.length; j++) {
if (map[i][j] == obc) {
// BFS 查找是否存在 NONE 边界
int len = 0;
List<KVPair> queue = new ArrayList<>();
queue.add(new KVPair(i, j));
List<KVPair> colored = new ArrayList<>();
colored.add(new KVPair(i, j));
boolean isDead = true;
while (len < queue.size()) {
// 获取当前的四周插入到queue
KVPair kvPair = queue.get(len);
int row = kvPair.k;
int col = kvPair.v;
// 上
if (kvPair.k != 0 && isAliveOrAdd(colored, map, row - 1, col, obc, queue)) {
isDead = false;
break;
}
// 下
if (kvPair.k != width && isAliveOrAdd(colored, map, row + 1, col, obc, queue)) {
isDead = false;
break;
}
// 左
if (kvPair.v != 0 && isAliveOrAdd(colored, map, row, col - 1, obc, queue)) {
isDead = false;
break;
}
// 右
if (kvPair.v != width && isAliveOrAdd(colored, map, row, col + 1, obc, queue)) {
isDead = false;
break;
}
len++;
}
if (isDead) {
// 去除操作
colored.forEach(item -> draw(Cross.NONE, item.k + 1, item.v + 1, map));
}
}
}
}
}
private static boolean isAliveOrAdd(List<KVPair> colored, Cross[][] map, int row, int col, Cross obc, List<KVPair> queue){
if (isColored(colored, row, col)) return false;
// 活的,则退出整个大循环
if (map[row][col] == Cross.NONE) {
return true;
}
// 为己方的,则假如队列,否则对方不进行操作
if (map[row][col] == obc) {
queue.add(new KVPair(row, col));
colored.add(new KVPair(row, col));
}
return false;
}
private static boolean isColored(List<KVPair> colored, int row, int col) {
for (KVPair tmp:colored) {
if (tmp.k == row && tmp.v == col) return true;
}
return false;
}
}

View File

@ -0,0 +1,37 @@
package top.dreamcenter.go.server;
import top.dreamcenter.go.msg.Cross;
public class DataUtil {
public static String parseData(Cross[][] map){
StringBuilder sb = new StringBuilder();
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map.length; j++) {
sb.append(i).append(j);
if (map[i][j] == Cross.NONE) {
sb.append(0);
} else if (map[i][j] == Cross.BLACK) {
sb.append(1);
} else {
sb.append(2);
}
}
}
return sb.toString();
}
public static Cross[][] rawData(String data){
Cross[][] cross = new Cross[9][9];
Cross[] colors = new Cross[]{Cross.NONE, Cross.BLACK, Cross.WHITE};
for (int i = 0; i < data.length();) {
int row = data.charAt(i) - '0';
int col = data.charAt(i + 1) - '0';
Cross color = colors[data.charAt(i + 2) - '0'];
cross[row][col] = color;
i += 3;
}
return cross;
}
}

View File

@ -0,0 +1,42 @@
package top.dreamcenter.go.server;
import top.dreamcenter.go.msg.Cross;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(1919);
while (true) {
AtomicBoolean cur = new AtomicBoolean(true); // true : black ; false : white
System.out.println("地图初始化 ...");
Cross[][] map = CalDataUtil.getInitMap();
System.out.println("初始化完成");
List<UserThread> list = new ArrayList<>();
System.out.println("等待用户1接入 ... ");
Socket u1 = serverSocket.accept();
System.out.println("用户1已连接");
UserThread u1t = new UserThread(u1, map, cur, Cross.BLACK, list);
u1t.start();
list.add(u1t);
System.out.println("等待用户2接入 ... ");
Socket u2 = serverSocket.accept();
System.out.println("用户2已连接");
UserThread u2t = new UserThread(u2, map, cur, Cross.WHITE, list);
u2t.start();
list.add(u2t);
}
}
}

View File

@ -0,0 +1,105 @@
package top.dreamcenter.go.server;
import top.dreamcenter.go.msg.Cross;
import top.dreamcenter.go.msg.NewClick;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class UserThread extends Thread{
private Socket socket;
private ObjectOutputStream os;
private ObjectInput oi;
private Cross[][] map;
private AtomicBoolean cur;
private Cross self;
private List<UserThread> users;
public boolean isAlive;
public UserThread(Socket socket, Cross[][] map, AtomicBoolean cur, Cross self, List<UserThread> list) {
this.socket = socket;
try {
os = new ObjectOutputStream(socket.getOutputStream());
oi = new ObjectInputStream(socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
this.map = map;
this.cur = cur;
this.self = self;
users = list;
isAlive = true;
}
@Override
public void run() {
try {
sendMsg(self);
} catch (IOException e) {
e.printStackTrace();
}
// 不间断接收消息
Thread thread = new Thread(() -> {
while (true) {
try {
Object o = recMsg();
NewClick newClick = (NewClick) o;
if (newClick.color == (cur.get() ? Cross.BLACK : Cross.WHITE) && newClick.color == self){
CalDataUtil.draw(newClick, map);
cur.set(!cur.get());
}
users.forEach(u -> {
try {
if (u.isAlive) u.sendMsg(DataUtil.parseData(map));
} catch (IOException e) {
e.printStackTrace();
}
});
} catch (Exception e){
System.out.println("::" + e.getMessage());
System.out.println("连接断开");
isAlive = false;
break;
}
}
});
thread.start();
// 线程等待
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public Socket getSocket() {
return socket;
}
public void sendMsg(Object obj) throws IOException {
os.writeObject(obj);
}
private Object recMsg() throws IOException, ClassNotFoundException {
return oi.readObject();
}
}