Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3fa433d224 | |||
| fb7c14c59a | |||
| c521256536 | |||
| ab6bb3d894 | |||
| eef25eb437 | |||
| ea46b30f54 | |||
| 4925ad4995 | |||
| b6f45fe17f | |||
| 9b747558bc |
299
app.py
299
app.py
@@ -8,11 +8,11 @@ app.config['SECRET_KEY'] = 'your_secret_key_here'
|
||||
socketio = SocketIO(app, cors_allowed_origins="*")
|
||||
|
||||
# 内存存储
|
||||
rooms = {} # room_id -> {name, password, public, board, turn, players, players_sid, watchers, user_color}
|
||||
users = {} # sid -> username
|
||||
username_to_sid = {} # username -> sid
|
||||
rooms = {}
|
||||
users = {}
|
||||
username_to_sid = {}
|
||||
|
||||
# 初始化棋盘
|
||||
# ---------- 棋盘初始化 ----------
|
||||
def init_board():
|
||||
return [
|
||||
['r', 'h', 'e', 'a', 'k', 'a', 'e', 'h', 'r'],
|
||||
@@ -30,21 +30,207 @@ def init_board():
|
||||
def board_to_string(board):
|
||||
return '\n'.join(''.join(row) for row in board)
|
||||
|
||||
# 中国象棋规则验证(保持不变)
|
||||
# ---------- 规则验证 ----------
|
||||
def is_valid_move(board, fr, fc, tr, tc, turn):
|
||||
# ... 保持原有的规则验证代码不变 ...
|
||||
# (这里省略,请保留之前的完整规则验证代码)
|
||||
pass
|
||||
"""检查移动是否合法"""
|
||||
# 边界检查
|
||||
if not (0 <= fr < 10 and 0 <= fc < 9 and 0 <= tr < 10 and 0 <= tc < 9):
|
||||
return False
|
||||
|
||||
piece = board[fr][fc]
|
||||
target = board[tr][tc]
|
||||
|
||||
# 起点不能为空
|
||||
if piece == '.':
|
||||
return False
|
||||
|
||||
# 检查是否属于当前方
|
||||
if turn == 'red' and not piece.isupper():
|
||||
return False
|
||||
if turn == 'black' and not piece.islower():
|
||||
return False
|
||||
|
||||
# 目标格不能有己方棋子
|
||||
if target != '.':
|
||||
if (turn == 'red' and target.isupper()) or (turn == 'black' and target.islower()):
|
||||
return False
|
||||
|
||||
p = piece.lower()
|
||||
dr = tr - fr
|
||||
dc = tc - fc
|
||||
|
||||
# ---------- 车 ----------
|
||||
if p == 'r':
|
||||
if fr != tr and fc != tc:
|
||||
return False
|
||||
step_r = 1 if dr > 0 else -1 if dr < 0 else 0
|
||||
step_c = 1 if dc > 0 else -1 if dc < 0 else 0
|
||||
r, c = fr + step_r, fc + step_c
|
||||
while (r, c) != (tr, tc):
|
||||
if board[r][c] != '.':
|
||||
return False
|
||||
r += step_r
|
||||
c += step_c
|
||||
return True
|
||||
|
||||
# ---------- 马 ----------
|
||||
elif p == 'h':
|
||||
if (abs(dr), abs(dc)) not in [(2, 1), (1, 2)]:
|
||||
return False
|
||||
# 绊马腿
|
||||
if abs(dr) == 2:
|
||||
block_r, block_c = fr + (1 if dr > 0 else -1), fc
|
||||
else:
|
||||
block_r, block_c = fr, fc + (1 if dc > 0 else -1)
|
||||
if board[block_r][block_c] != '.':
|
||||
return False
|
||||
return True
|
||||
|
||||
# ---------- 相/象 ----------
|
||||
elif p == 'e':
|
||||
if abs(dr) != 2 or abs(dc) != 2:
|
||||
return False
|
||||
# 不能过河
|
||||
if turn == 'red' and tr < 5: # 红相不能过河到对方半场
|
||||
return False
|
||||
if turn == 'black' and tr > 4: # 黑象不能过河
|
||||
return False
|
||||
# 象眼
|
||||
block_r, block_c = fr + (1 if dr > 0 else -1), fc + (1 if dc > 0 else -1)
|
||||
if board[block_r][block_c] != '.':
|
||||
return False
|
||||
return True
|
||||
|
||||
# ---------- 士 ----------
|
||||
elif p == 'a':
|
||||
if abs(dr) != 1 or abs(dc) != 1:
|
||||
return False
|
||||
# 九宫
|
||||
if turn == 'red':
|
||||
if tr < 7 or tc < 3 or tc > 5:
|
||||
return False
|
||||
else:
|
||||
if tr > 2 or tc < 3 or tc > 5:
|
||||
return False
|
||||
return True
|
||||
|
||||
# ---------- 将/帅 ----------
|
||||
elif p == 'k':
|
||||
if abs(dr) + abs(dc) != 1:
|
||||
return False
|
||||
# 九宫
|
||||
if turn == 'red':
|
||||
if tr < 7 or tc < 3 or tc > 5:
|
||||
return False
|
||||
else:
|
||||
if tr > 2 or tc < 3 or tc > 5:
|
||||
return False
|
||||
return True
|
||||
|
||||
# ---------- 炮 ----------
|
||||
elif p == 'c':
|
||||
if fr != tr and fc != tc:
|
||||
return False
|
||||
step_r = 1 if dr > 0 else -1 if dr < 0 else 0
|
||||
step_c = 1 if dc > 0 else -1 if dc < 0 else 0
|
||||
r, c = fr + step_r, fc + step_c
|
||||
cnt = 0
|
||||
while (r, c) != (tr, tc):
|
||||
if board[r][c] != '.':
|
||||
cnt += 1
|
||||
r += step_r
|
||||
c += step_c
|
||||
# 吃子:需要恰好一个炮架
|
||||
if target != '.':
|
||||
return cnt == 1
|
||||
# 不吃子:路径必须为空
|
||||
else:
|
||||
return cnt == 0
|
||||
|
||||
# ---------- 兵/卒 ----------
|
||||
elif p == 'p':
|
||||
if turn == 'red':
|
||||
if dr > 0: # 不能后退
|
||||
return False
|
||||
# 未过河(行 > 4)只能前进
|
||||
if fr > 4:
|
||||
return dc == 0 and dr == -1
|
||||
else: # 过河
|
||||
return (dr == -1 and dc == 0) or (dr == 0 and abs(dc) == 1)
|
||||
else: # 黑方
|
||||
if dr < 0:
|
||||
return False
|
||||
if fr < 5: # 未过河
|
||||
return dc == 0 and dr == 1
|
||||
else: # 过河
|
||||
return (dr == 1 and dc == 0) or (dr == 0 and abs(dc) == 1)
|
||||
|
||||
return False
|
||||
|
||||
def is_check(board, turn):
|
||||
# ... 保持原有的将判断代码不变 ...
|
||||
pass
|
||||
"""检查 turn 方是否被将"""
|
||||
# 找出将帅位置
|
||||
red_king = black_king = None
|
||||
for i in range(10):
|
||||
for j in range(9):
|
||||
if board[i][j] == 'K':
|
||||
red_king = (i, j)
|
||||
elif board[i][j] == 'k':
|
||||
black_king = (i, j)
|
||||
|
||||
if turn == 'red':
|
||||
if not red_king:
|
||||
return True # 红方无帅,输
|
||||
# 检查黑方所有棋子能否吃到红帅
|
||||
for i in range(10):
|
||||
for j in range(9):
|
||||
piece = board[i][j]
|
||||
if piece.islower() and piece != '.':
|
||||
if is_valid_move(board, i, j, red_king[0], red_king[1], 'black'):
|
||||
return True
|
||||
# 将帅对面
|
||||
if black_king and red_king[1] == black_king[1]:
|
||||
c = red_king[1]
|
||||
r1, r2 = sorted([red_king[0], black_king[0]])
|
||||
for r in range(r1 + 1, r2):
|
||||
if board[r][c] != '.':
|
||||
break
|
||||
else:
|
||||
return True # 对面
|
||||
return False
|
||||
else:
|
||||
if not black_king:
|
||||
return True
|
||||
for i in range(10):
|
||||
for j in range(9):
|
||||
piece = board[i][j]
|
||||
if piece.isupper() and piece != '.':
|
||||
if is_valid_move(board, i, j, black_king[0], black_king[1], 'red'):
|
||||
return True
|
||||
if red_king and red_king[1] == black_king[1]:
|
||||
c = red_king[1]
|
||||
r1, r2 = sorted([red_king[0], black_king[0]])
|
||||
for r in range(r1 + 1, r2):
|
||||
if board[r][c] != '.':
|
||||
break
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
def make_move(board, fr, fc, tr, tc, turn):
|
||||
# ... 保持原有的移动代码不变 ...
|
||||
pass
|
||||
"""执行移动,如果合法返回新棋盘,否则返回 None"""
|
||||
if not is_valid_move(board, fr, fc, tr, tc, turn):
|
||||
return None
|
||||
new_board = copy.deepcopy(board)
|
||||
piece = new_board[fr][fc]
|
||||
new_board[fr][fc] = '.'
|
||||
new_board[tr][tc] = piece
|
||||
# 检查移动后是否导致己方被将
|
||||
if is_check(new_board, turn):
|
||||
return None
|
||||
return new_board
|
||||
|
||||
# 路由
|
||||
# ---------- 路由 ----------
|
||||
@app.route('/')
|
||||
def lobby():
|
||||
return render_template('lobby.html')
|
||||
@@ -55,7 +241,7 @@ def room_page(room_id):
|
||||
return "房间不存在", 404
|
||||
return render_template('room.html', room_id=room_id)
|
||||
|
||||
# SocketIO 事件
|
||||
# ---------- SocketIO 事件 ----------
|
||||
@socketio.on('connect')
|
||||
def handle_connect():
|
||||
print('Client connected', request.sid)
|
||||
@@ -131,7 +317,7 @@ def handle_create_room(data):
|
||||
public = data['public']
|
||||
password = data.get('password', '') if not public else ''
|
||||
room_id = str(uuid.uuid4())[:8]
|
||||
|
||||
|
||||
rooms[room_id] = {
|
||||
'name': room_name,
|
||||
'password': password,
|
||||
@@ -143,13 +329,12 @@ def handle_create_room(data):
|
||||
'watchers': {},
|
||||
'user_color': {}
|
||||
}
|
||||
|
||||
# 创建者自动加入房间(红方)
|
||||
|
||||
join_room(room_id)
|
||||
rooms[room_id]['players']['red'] = username
|
||||
rooms[room_id]['players_sid']['red'] = request.sid
|
||||
rooms[room_id]['user_color'][username] = 'red'
|
||||
|
||||
|
||||
emit('room_created', {'room_id': room_id}, room=request.sid)
|
||||
broadcast_rooms()
|
||||
|
||||
@@ -159,33 +344,30 @@ def handle_join_room(data):
|
||||
if not username:
|
||||
emit('error', {'msg': '未登录'})
|
||||
return
|
||||
|
||||
|
||||
room_id = data['room_id']
|
||||
password = data.get('password', '')
|
||||
|
||||
|
||||
if room_id not in rooms:
|
||||
emit('error', {'msg': '房间不存在'})
|
||||
return
|
||||
|
||||
|
||||
room = rooms[room_id]
|
||||
|
||||
# 验证密码
|
||||
|
||||
if room['password'] and room['password'] != password:
|
||||
emit('error', {'msg': '密码错误'})
|
||||
return
|
||||
|
||||
# 检查用户是否已经在房间中(重连)
|
||||
|
||||
# 重连处理
|
||||
if username in room['user_color']:
|
||||
color = room['user_color'][username]
|
||||
old_sid = room['players_sid'][color]
|
||||
if old_sid and old_sid != request.sid:
|
||||
# 通知旧连接被踢出
|
||||
emit('force_disconnect', {'msg': '您在另一个窗口加入了房间'}, room=old_sid)
|
||||
disconnect(old_sid)
|
||||
|
||||
|
||||
room['players_sid'][color] = request.sid
|
||||
join_room(room_id)
|
||||
|
||||
emit('room_joined', {
|
||||
'board': board_to_string(room['board']),
|
||||
'turn': room['turn'],
|
||||
@@ -194,24 +376,20 @@ def handle_join_room(data):
|
||||
'watchers': len(room['watchers'])
|
||||
}, room=request.sid)
|
||||
return
|
||||
|
||||
# 新用户加入
|
||||
# 检查红方位置是否可用
|
||||
|
||||
# 分配颜色
|
||||
if room['players']['red'] is None:
|
||||
color = 'red'
|
||||
# 检查黑方位置是否可用
|
||||
elif room['players']['black'] is None:
|
||||
color = 'black'
|
||||
else:
|
||||
color = None # 旁观者
|
||||
|
||||
color = None
|
||||
|
||||
if color:
|
||||
# 玩家身份
|
||||
room['players'][color] = username
|
||||
room['players_sid'][color] = request.sid
|
||||
room['user_color'][username] = color
|
||||
join_room(room_id)
|
||||
|
||||
emit('room_joined', {
|
||||
'board': board_to_string(room['board']),
|
||||
'turn': room['turn'],
|
||||
@@ -219,17 +397,11 @@ def handle_join_room(data):
|
||||
'players': room['players'],
|
||||
'watchers': len(room['watchers'])
|
||||
}, room=request.sid)
|
||||
|
||||
# 通知房间内其他玩家
|
||||
emit('player_joined', {'color': color, 'username': username}, room=room_id, include_self=False)
|
||||
|
||||
print(f"玩家 {username} 加入房间 {room_id} 作为 {color}")
|
||||
|
||||
else:
|
||||
# 旁观者
|
||||
room['watchers'][request.sid] = username
|
||||
join_room(room_id)
|
||||
|
||||
emit('room_joined', {
|
||||
'board': board_to_string(room['board']),
|
||||
'turn': room['turn'],
|
||||
@@ -237,11 +409,9 @@ def handle_join_room(data):
|
||||
'players': room['players'],
|
||||
'watchers': len(room['watchers'])
|
||||
}, room=request.sid)
|
||||
|
||||
emit('watcher_joined', {'username': username}, room=room_id, include_self=False)
|
||||
|
||||
print(f"玩家 {username} 加入房间 {room_id} 作为旁观者")
|
||||
|
||||
|
||||
broadcast_rooms()
|
||||
|
||||
@socketio.on('move')
|
||||
@@ -251,40 +421,49 @@ def handle_move(data):
|
||||
if not username:
|
||||
emit('error', {'msg': '未登录'})
|
||||
return
|
||||
|
||||
|
||||
room_id = data['room_id']
|
||||
if room_id not in rooms:
|
||||
emit('error', {'msg': '房间不存在'})
|
||||
return
|
||||
|
||||
|
||||
room = rooms[room_id]
|
||||
|
||||
# 检查是否轮到该玩家
|
||||
|
||||
# 回合检查
|
||||
if room['turn'] == 'red' and room['players_sid']['red'] != sid:
|
||||
emit('error', {'msg': '不是你的回合'})
|
||||
emit('move_result', {'success': False, 'message': '不是你的回合'})
|
||||
return
|
||||
if room['turn'] == 'black' and room['players_sid']['black'] != sid:
|
||||
emit('error', {'msg': '不是你的回合'})
|
||||
emit('move_result', {'success': False, 'message': '不是你的回合'})
|
||||
return
|
||||
|
||||
|
||||
fr, fc = data['from']['row'], data['from']['col']
|
||||
tr, tc = data['to']['row'], data['to']['col']
|
||||
|
||||
print(f"玩家 {username} 移动: 从 ({fr},{fc}) 到 ({tr},{tc})")
|
||||
|
||||
|
||||
print(f"玩家 {username} 尝试移动: ({fr},{fc}) -> ({tr},{tc})")
|
||||
print(f"当前棋盘:")
|
||||
for row in room['board']:
|
||||
print(' '.join(row))
|
||||
|
||||
new_board = make_move(room['board'], fr, fc, tr, tc, room['turn'])
|
||||
if new_board is None:
|
||||
emit('error', {'msg': '非法移动'})
|
||||
emit('move_result', {'success': False, 'message': '非法移动'})
|
||||
return
|
||||
|
||||
|
||||
room['board'] = new_board
|
||||
room['turn'] = 'black' if room['turn'] == 'red' else 'red'
|
||||
|
||||
# 广播新棋盘
|
||||
|
||||
print(f"移动成功,新棋盘:")
|
||||
for row in room['board']:
|
||||
print(' '.join(row))
|
||||
print(f"下一回合: {room['turn']}")
|
||||
|
||||
emit('board_updated', {
|
||||
'board': board_to_string(room['board']),
|
||||
'turn': room['turn']
|
||||
}, room=room_id)
|
||||
|
||||
emit('move_result', {'success': True}, room=sid)
|
||||
|
||||
if __name__ == '__main__':
|
||||
socketio.run(app, debug=True, host='0.0.0.0', port=5000)
|
||||
socketio.run(app, debug=True,allow_unsafe_werkzeug=True, host='0.0.0.0', port=5000)
|
||||
206
static/room.js
206
static/room.js
@@ -8,7 +8,7 @@ if (!username) {
|
||||
}
|
||||
|
||||
const socket = io();
|
||||
let myColor = null; // 'red', 'black', null(旁观)
|
||||
let myColor = null; // 'red', 'black', null
|
||||
let board = [];
|
||||
let turn = 'red';
|
||||
let selectedRow = -1, selectedCol = -1;
|
||||
@@ -16,58 +16,52 @@ let selectedRow = -1, selectedCol = -1;
|
||||
const canvas = document.getElementById('chessboard');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const cellSize = 60;
|
||||
const offset = 30; // 棋盘边距
|
||||
const offset = 30;
|
||||
|
||||
// 修复坐标计算函数
|
||||
// 精确坐标转换
|
||||
function getBoardPosition(clientX, clientY) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
|
||||
// 计算点击位置相对于canvas画布的比例
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
|
||||
// 计算在canvas坐标系中的实际坐标
|
||||
const canvasX = (clientX - rect.left) * scaleX;
|
||||
const canvasY = (clientY - rect.top) * scaleY;
|
||||
|
||||
// 减去偏移量后计算格子索引
|
||||
const col = Math.floor((canvasX - offset) / cellSize);
|
||||
const row = Math.floor((canvasY - offset) / cellSize);
|
||||
|
||||
const x = canvasX - offset;
|
||||
const y = canvasY - offset;
|
||||
const col = Math.floor(x / cellSize);
|
||||
const row = Math.floor(y / cellSize);
|
||||
if (x < 0 || y < 0 || col >= 9 || row >= 10) {
|
||||
return { row: -1, col: -1 };
|
||||
}
|
||||
return { row, col };
|
||||
}
|
||||
|
||||
// 绘制棋盘
|
||||
function drawBoard() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// 画棋盘线
|
||||
|
||||
// 画线
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = '#000';
|
||||
|
||||
// 画横线
|
||||
ctx.strokeStyle = '#8B4513';
|
||||
for (let i = 0; i < 10; i++) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(offset, offset + i * cellSize);
|
||||
ctx.lineTo(offset + 8 * cellSize, offset + i * cellSize);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// 画竖线
|
||||
for (let i = 0; i < 9; i++) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(offset + i * cellSize, offset);
|
||||
ctx.lineTo(offset + i * cellSize, offset + 9 * cellSize);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// 画楚河汉界
|
||||
|
||||
// 楚河汉界
|
||||
ctx.font = 'bold 24px "KaiTi"';
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.fillText('楚 河', offset + 2 * cellSize, offset + 4.5 * cellSize);
|
||||
ctx.fillText('汉 界', offset + 2 * cellSize, offset + 5.5 * cellSize);
|
||||
|
||||
// 画九宫对角线
|
||||
ctx.fillStyle = '#8B4513';
|
||||
ctx.fillText('楚 河', offset + 2 * cellSize, offset + 4.5 * cellSize - 5);
|
||||
ctx.fillText('汉 界', offset + 2 * cellSize, offset + 5.5 * cellSize - 5);
|
||||
|
||||
// 九宫对角线
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(offset + 3 * cellSize, offset);
|
||||
ctx.lineTo(offset + 5 * cellSize, offset + 2 * cellSize);
|
||||
@@ -93,26 +87,19 @@ function drawBoard() {
|
||||
if (piece && piece !== '.') {
|
||||
const x = offset + c * cellSize;
|
||||
const y = offset + r * cellSize;
|
||||
|
||||
// 画棋子圆形
|
||||
|
||||
// 棋子圆形
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, cellSize/2 - 4, 0, 2 * Math.PI);
|
||||
|
||||
// 根据棋子颜色设置背景
|
||||
if (piece === piece.toUpperCase()) {
|
||||
ctx.fillStyle = '#ffcccc'; // 红方
|
||||
} else {
|
||||
ctx.fillStyle = '#ccccff'; // 黑方
|
||||
}
|
||||
ctx.fillStyle = piece === piece.toUpperCase() ? '#FF6B6B' : '#4A90E2';
|
||||
ctx.fill();
|
||||
|
||||
ctx.strokeStyle = '#000';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.stroke();
|
||||
|
||||
// 画棋子文字
|
||||
|
||||
// 棋子文字
|
||||
ctx.font = 'bold 28px "KaiTi"';
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.fillStyle = '#FFF';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
let char = '';
|
||||
@@ -129,63 +116,80 @@ function drawBoard() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 高亮选中的棋子
|
||||
|
||||
// 高亮选中的棋子(金色外圈 + 准星)
|
||||
if (selectedRow >= 0 && selectedCol >= 0) {
|
||||
const x = offset + selectedCol * cellSize;
|
||||
const y = offset + selectedRow * cellSize;
|
||||
|
||||
// 外发光
|
||||
ctx.beginPath();
|
||||
ctx.arc(offset + selectedCol * cellSize, offset + selectedRow * cellSize, cellSize/2 - 4, 0, 2 * Math.PI);
|
||||
ctx.strokeStyle = '#ff0000';
|
||||
ctx.lineWidth = 4;
|
||||
ctx.arc(x, y, cellSize/2 - 2, 0, 2 * Math.PI);
|
||||
ctx.strokeStyle = '#FFD700';
|
||||
ctx.lineWidth = 6;
|
||||
ctx.stroke();
|
||||
|
||||
// 准星(十字线)
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - 15, y);
|
||||
ctx.lineTo(x + 15, y);
|
||||
ctx.moveTo(x, y - 15);
|
||||
ctx.lineTo(x, y + 15);
|
||||
ctx.strokeStyle = '#FFD700';
|
||||
ctx.lineWidth = 3;
|
||||
ctx.stroke();
|
||||
|
||||
// 中心点
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, 4, 0, 2 * Math.PI);
|
||||
ctx.fillStyle = '#FFD700';
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标点击事件
|
||||
// 鼠标点击
|
||||
canvas.addEventListener('click', (e) => {
|
||||
const pos = getBoardPosition(e.clientX, e.clientY);
|
||||
const row = pos.row;
|
||||
const col = pos.col;
|
||||
|
||||
console.log('点击位置:', row, col);
|
||||
console.log('我的身份:', myColor);
|
||||
console.log('当前回合:', turn);
|
||||
|
||||
// 检查点击是否在棋盘内
|
||||
if (row < 0 || row >= 10 || col < 0 || col >= 9) {
|
||||
console.log('点击在棋盘外');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是旁观者,不能走棋
|
||||
if (row < 0 || row >= 10 || col < 0 || col >= 9) return;
|
||||
|
||||
const clickedPiece = board[row] && board[row][col];
|
||||
|
||||
// 旁观者
|
||||
if (myColor === null) {
|
||||
alert('您是旁观者,不能走棋');
|
||||
if (clickedPiece && clickedPiece !== '.') {
|
||||
alert('您是旁观者,只能观看');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果不是当前玩家回合,不能走棋
|
||||
// 回合检查
|
||||
if (myColor !== turn) {
|
||||
alert(`现在轮到${turn === 'red' ? '红方' : '黑方'},请您等待`);
|
||||
if (clickedPiece && clickedPiece !== '.') {
|
||||
alert(`现在轮到${turn === 'red' ? '红方' : '黑方'},请等待`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedRow === -1) {
|
||||
// 第一次点击:选中棋子
|
||||
const piece = board[row] && board[row][col];
|
||||
if (piece && piece !== '.') {
|
||||
// 检查是否选中了自己的棋子
|
||||
if ((myColor === 'red' && piece === piece.toUpperCase()) ||
|
||||
(myColor === 'black' && piece === piece.toLowerCase())) {
|
||||
// 第一次点击:选择己方棋子
|
||||
if (clickedPiece && clickedPiece !== '.') {
|
||||
const isMyPiece = (myColor === 'red' && clickedPiece === clickedPiece.toUpperCase()) ||
|
||||
(myColor === 'black' && clickedPiece === clickedPiece.toLowerCase());
|
||||
if (isMyPiece) {
|
||||
selectedRow = row;
|
||||
selectedCol = col;
|
||||
drawBoard();
|
||||
console.log('选中棋子:', piece, '位置:', row, col);
|
||||
console.log('选中:', clickedPiece, row, col);
|
||||
} else {
|
||||
alert('不能选择对方的棋子');
|
||||
alert('不能选择对方棋子');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 第二次点击:移动棋子
|
||||
console.log('尝试移动从', selectedRow, selectedCol, '到', row, col);
|
||||
// 第二次点击:尝试移动
|
||||
console.log('移动请求:', selectedRow, selectedCol, '->', row, col);
|
||||
socket.emit('move', {
|
||||
room_id: roomId,
|
||||
from: { row: selectedRow, col: selectedCol },
|
||||
@@ -197,73 +201,57 @@ canvas.addEventListener('click', (e) => {
|
||||
|
||||
// Socket 事件
|
||||
socket.on('connect', () => {
|
||||
console.log('连接成功');
|
||||
socket.emit('login', { username });
|
||||
socket.emit('join_room', { room_id: roomId, password });
|
||||
});
|
||||
|
||||
socket.on('room_joined', (data) => {
|
||||
console.log('加入房间成功:', data);
|
||||
|
||||
// 解析棋盘
|
||||
if (data.board) {
|
||||
board = data.board.split('\n').map(row => row.split(''));
|
||||
}
|
||||
|
||||
board = data.board.split('\n').map(row => row.split(''));
|
||||
turn = data.turn;
|
||||
myColor = data.my_color;
|
||||
|
||||
// 更新UI
|
||||
document.getElementById('my-color').innerText = myColor ? (myColor === 'red' ? '红方' : '黑方') : '旁观';
|
||||
document.getElementById('red-player').innerText = data.players.red || '等待';
|
||||
document.getElementById('black-player').innerText = data.players.black || '等待';
|
||||
document.getElementById('watcher-count').innerText = data.watchers;
|
||||
document.getElementById('turn').innerText = turn === 'red' ? '红方' : '黑方';
|
||||
|
||||
// 清除选中状态
|
||||
selectedRow = -1;
|
||||
selectedCol = -1;
|
||||
|
||||
// 重新绘制
|
||||
drawBoard();
|
||||
|
||||
console.log('我的身份:', myColor);
|
||||
});
|
||||
|
||||
socket.on('board_updated', (data) => {
|
||||
console.log('棋盘更新:', data);
|
||||
board = data.board.split('\n').map(row => row.split(''));
|
||||
turn = data.turn;
|
||||
document.getElementById('turn').innerText = turn === 'red' ? '红方' : '黑方';
|
||||
|
||||
// 清除选中状态
|
||||
selectedRow = -1;
|
||||
selectedCol = -1;
|
||||
|
||||
drawBoard();
|
||||
});
|
||||
|
||||
socket.on('player_joined', (data) => {
|
||||
console.log('玩家加入:', data);
|
||||
if (data.color === 'red') {
|
||||
document.getElementById('red-player').innerText = data.username;
|
||||
} else if (data.color === 'black') {
|
||||
document.getElementById('black-player').innerText = data.username;
|
||||
socket.on('move_result', (data) => {
|
||||
if (data.success) {
|
||||
console.log('移动成功');
|
||||
selectedRow = -1; // 清除选中
|
||||
drawBoard();
|
||||
} else {
|
||||
alert(data.message || '非法移动');
|
||||
// 保持选中,但可以闪烁提示
|
||||
drawBoard(); // 重绘保持高亮
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('player_joined', (data) => {
|
||||
if (data.color === 'red') document.getElementById('red-player').innerText = data.username;
|
||||
else document.getElementById('black-player').innerText = data.username;
|
||||
});
|
||||
|
||||
socket.on('player_left', (data) => {
|
||||
console.log('玩家离开:', data);
|
||||
if (data.color === 'red') {
|
||||
document.getElementById('red-player').innerText = '等待';
|
||||
} else if (data.color === 'black') {
|
||||
document.getElementById('black-player').innerText = '等待';
|
||||
}
|
||||
if (data.color === 'red') document.getElementById('red-player').innerText = '等待';
|
||||
else document.getElementById('black-player').innerText = '等待';
|
||||
});
|
||||
|
||||
socket.on('error', (data) => {
|
||||
console.error('错误:', data.msg);
|
||||
alert(data.msg);
|
||||
selectedRow = -1;
|
||||
drawBoard();
|
||||
});
|
||||
|
||||
socket.on('force_disconnect', (data) => {
|
||||
@@ -272,21 +260,13 @@ socket.on('force_disconnect', (data) => {
|
||||
window.location.href = '/';
|
||||
});
|
||||
|
||||
// 复制链接
|
||||
document.getElementById('copy-link').addEventListener('click', () => {
|
||||
const link = window.location.href;
|
||||
navigator.clipboard.writeText(link).then(() => {
|
||||
alert('链接已复制到剪贴板');
|
||||
}).catch(() => {
|
||||
alert('复制失败,请手动复制');
|
||||
});
|
||||
navigator.clipboard.writeText(window.location.href).then(() => alert('链接已复制'));
|
||||
});
|
||||
|
||||
// 添加键盘事件,按ESC取消选中
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
selectedRow = -1;
|
||||
selectedCol = -1;
|
||||
drawBoard();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user