【数组】999. 可以被一步捕获的棋子数(简单)

本文介绍了一个算法问题,目标是在一个8x8的棋盘上找到由白色车(Rook)一步可捕获的所有黑色卒的位置。文章通过示例详细解释了车的移动规则,并提供了一段C++代码实现。

【题目】
在一个 8 x 8 的棋盘上,有一个白色的车(Rook),用字符 ‘R’ 表示。棋盘上还可能存在空方块,白色的象(Bishop)以及黑色的卒(pawn),分别用字符 ‘.’,‘B’ 和 ‘p’ 表示。不难看出,大写字符表示的是白棋,小写字符表示的是黑棋。
车按国际象棋中的规则移动。东,西,南,北四个基本方向任选其一,然后一直向选定的方向移动,直到满足下列四个条件之一:
棋手选择主动停下来。
棋子因到达棋盘的边缘而停下。
棋子移动到某一方格来捕获位于该方格上敌方(黑色)的卒,停在该方格内。
车不能进入/越过已经放有其他友方棋子(白色的象)的方格,停在友方棋子前。
你现在可以控制车移动一次,请你统计有多少敌方的卒处于你的捕获范围内(即,可以被一步捕获的棋子数)。
【示例 1】
输入:[[".",".",".",".",".",".",".","."],[".",".",".",“p”,".",".",".","."],[".",".",".",“R”,".",".",".",“p”],[".",".",".",".",".",".",".","."],[".",".",".",".",".",".",".","."],[".",".",".",“p”,".",".",".","."],[".",".",".",".",".",".",".","."],[".",".",".",".",".",".",".","."]]
输出:3
解释:
在本例中,车能够捕获所有的卒。
【示例 2】
输入:[[".",".",".",".",".",".",".","."],[".",“p”,“p”,“p”,“p”,“p”,".","."],[".",“p”,“p”,“B”,“p”,“p”,".","."],[".",“p”,“B”,“R”,“B”,“p”,".","."],[".",“p”,“p”,“B”,“p”,“p”,".","."],[".",“p”,“p”,“p”,“p”,“p”,".","."],[".",".",".",".",".",".",".","."],[".",".",".",".",".",".",".","."]]
输出:0
解释:
象阻止了车捕获任何卒。
【示例 3】
输入:[[".",".",".",".",".",".",".","."],[".",".",".",“p”,".",".",".","."],[".",".",".",“p”,".",".",".","."],[“p”,“p”,".",“R”,".",“p”,“B”,"."],[".",".",".",".",".",".",".","."],[".",".",".",“B”,".",".",".","."],[".",".",".",“p”,".",".",".","."],[".",".",".",".",".",".",".","."]]
输出:3
解释:
车可以捕获位置 b5,d6 和 f5 的卒。
【提示】
board.length == board[i].length == 8
board[i][j] 可以是 ‘R’,’.’,‘B’ 或 ‘p’
只有一个格子上存在 board[i][j] == ‘R’
【代码】

class Solution {
public:
    int numRookCaptures(vector<vector<char>>& board) {
        int fangxiang[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
        int Rx=0,Ry=0,cnt=0;
        for(int x=0;x<board.size();x++)
            for(int y=0;y<board[0].size();y++)
                if(board[x][y]=='R'){
                    Rx=x;
                    Ry=y;
                }

        for(int c=0;c<4;c++){
            int x=Rx,y=Ry;
            for(int i=0;i<8;i++){
                x+=fangxiang[c][0];
                y+=fangxiang[c][1];
                if(x>=0&&x<8&&y>=0&&y<8){//没有越界
                    if(board[x][y]=='p'){
                        cnt++;
                        break;
                    }else if(board[x][y]=='B')
                        break;
                }
            }
        }
        return cnt;
    }
};
<think> 我们正在分析一段实现MouseListener接口的Java代码,该代码涉及棋盘落子逻辑。 由于用户没有提供具体的代码,我将基于常见的棋盘落子逻辑实现中可能出现的问题和优化点进行一般性分析。 通常,这样的代码会包含一个棋盘面板(可能继承自JPanel),并实现MouseListener以响应鼠标点击事件,在点击位置放置棋子。 常见问题: 1. 性能问题:每次重绘整个棋盘,如果棋盘很大,可能造成性能浪费。优化:只重绘改变的部分(例如,只重绘刚下的棋子位置),但注意如果棋子有相互覆盖(如围棋提子)则仍需重绘整个区域。 2. 代码结构问题:将游戏逻辑和界面逻辑混杂在一起,导致难以维护和扩展。优化:使用MVC模式分离模型(棋盘状态)、视图(绘制)和控制器(事件处理)。 3. 落点计算:将鼠标点击坐标转换为棋盘格子坐标时,可能存在精度问题(如整除法导致坐标偏移)。建议使用浮点计算并四舍五入。 4. 未考虑禁手规则:在五子棋、围棋等游戏中,某些落子位置可能是被禁止的(如五子棋的禁手),代码中可能缺少规则校验。 5. 棋子重复放置:同一个位置可能被重复放置棋子,需要检查该位置是否已有棋子。 6. 线程问题:如果落子后需要进行长时间的计算(如AI落子),可能会阻塞事件调度线程(EDT),导致界面冻结。优化:使用SwingWorker在后台线程进行计算。 7. 资源管理:棋子图片等资源可能被重复加载,造成资源浪费。优化:使用静态变量或缓存机制。 8. 内存泄漏:如果使用了监听器,但没有在不需要时移除,可能导致内存泄漏。确保在组件销毁时移除监听器。 优化建议: 1. 分离逻辑:将游戏规则逻辑与界面分离,这样便于更换界面或逻辑,也便于测试。 2. 使用双缓冲:避免绘制时闪烁,JPanel默认双缓冲,但自定义绘制时需确保开启。 3. 使用缓存:对于棋盘背景等不变的部分,可以缓存为图像,每次重绘时直接绘制缓存图像。 4. 提高坐标转换精度:使用更精确的转换算法,例如棋盘每个格子的宽度为cellWidth,则格子坐标(x, y)可以通过以下方式计算: int x = (int) Math.round((e.getX() - margin) / (double)cellWidth); int y = (int) Math.round((e.getY() - margin) / (double)cellWidth); 5. 增加悔棋功能:通过栈保存每一步的状态,便于悔棋。 6. 添加音效和动画:提升用户体验。 功能解释: 实现MouseListener接口的类需要实现5个方法:mouseClicked, mousePressed, mouseReleased, mouseEntered, mouseExited。通常,我们只关心mouseClicked或mousePressed(根据需求)。在方法中,获取鼠标事件的位置,转换成棋盘坐标,然后判断是否有效落子(如是否在棋盘内,该位置是否为空),然后落子并重绘。 由于没有具体代码,以上为一般性分析。 相关问题: 1. 如何优化棋盘游戏的绘制性能? 2. MVC模式在棋盘游戏开发中如何应用? 3. 如何实现五子棋的禁手规则? </think> ### 棋盘落子逻辑代码分析与优化(基于MouseListener实现) #### 功能分析 1. **核心逻辑**: - 通过`mouseClicked()`方法捕获鼠标点击事件 - 将物理坐标转换为棋盘逻辑坐标: $$ \text{棋盘坐标} = \bigg\lfloor \frac{\text{鼠标坐标} - \text{棋盘偏移}}{\text{格子尺寸}} \bigg\rfloor $$ - 实现落子状态切换(如黑白棋交替落子) - 通过`repaint()`触发棋盘重绘 2. **典型代码结构**: ```java class ChessBoard extends JPanel implements MouseListener { private int[][] board; // 棋盘状态数组 private boolean isBlackTurn = true; // 当前落子方 public void mouseClicked(MouseEvent e) { int x = (e.getX() - MARGIN) / CELL_SIZE; int y = (e.getY() - MARGIN) / CELL_SIZE; if (isValidPosition(x, y) && board[y][x] == EMPTY) { board[y][x] = isBlackTurn ? BLACK : WHITE; isBlackTurn = !isBlackTurn; repaint(); // 重绘棋盘 } } // 其他MouseListener方法可留空... } ``` #### 常见问题及改进建议 1. **性能问题**: - **全盘重绘**:`repaint()`会刷新整个面板,建议使用局部重绘: ```java repaint(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE); ``` - **重复计算**:棋盘偏移量/格子尺寸应声明为`final`常量 2. **精度缺陷**: - **坐标转换错误**:整除法导致边缘点击坐标计算错误 *优化方案*: ```java int x = Math.round((float)(e.getX() - MARGIN) / CELL_SIZE); ``` 3. **逻辑漏洞**: - 缺少胜利条件判断(需添加胜负检查方法) - 无落子历史记录(建议引入栈存储棋步实现悔棋功能) 4. **代码健壮性**: - 添加空棋盘判断:`if (board == null) return;` - 增加点击响应阈值(防止连击): ```java private long lastClickTime = 0; if (System.currentTimeMillis() - lastClickTime < 200) return; ``` 5. **架构优化**: - **分离关注点**:将游戏逻辑从UI抽离为独立类(MVC模式) - **事件处理**:改用`MouseAdapter`避免实现所有接口方法 #### 完整优化示例 ```java class GameLogic { // 独立游戏逻辑层 public boolean placeStone(int x, int y, int player) { if (board[y][x] != EMPTY) return false; board[y][x] = player; return checkWin(x, y); // 胜负检查 } } class ChessBoard extends JPanel { private final GameLogic game = new GameLogic(); public void mouseClicked(MouseEvent e) { Point p = convertToBoardPos(e.getPoint()); if (p == null) return; if (game.placeStone(p.x, p.y, currentPlayer())) { repaintCell(p.x, p.y); // 局部重绘 } } } ``` #### 最佳实践建议 1. **添加视觉反馈**: - 悬停高亮:在`mouseMoved()`中实时显示可落子位置 - 落子动画:使用`JavaFX`或自定义动画线程 2. **线程安全**: 长时间运算(如AI计算)需启用`SwingWorker`防止界面冻结 3. **扩展性设计**: - 通过`BoardState`接口支持不同棋类规则 - 使用观察者模式实现游戏事件通知 [^1]: Java GUI事件处理官方文档 [^2]: MVC设计模式在游戏开发中的应用实践
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值