在当今快速发展的科技时代,人工智能(AI)已经渗透到各个领域,软件开发也不例外。AI编程工具的出现不仅改变了传统的编码方式,还大大提高了开发效率和代码质量。在这场技术革命中,Deepseek和Qwen-2.5作为代表性的AI编程工具,展现了各自独特的功能和优势。
Deepseek以其快速响应和直观的代码生成能力著称,特别适合需要迅速完成原型开发的场景。它能够根据用户的需求,快速生成相应的代码片段或完整的项目结构,极大地简化了开发流程。而对于复杂任务的支持,则是Qwen-2.5的强项。支持多语言编程的Qwen-2.5在处理复杂的逻辑和优化代码性能方面表现出色,为开发者提供了更多可能性。
通过详细解析Deepseek和Qwen-2.5在实现扫雷游戏过程中的表现,我们将探讨它们各自的优势与不足。这不仅有助于理解AI编程工具的实际应用效果,也为开发者选择合适的开发工具有所启发。
第一部分:评测模型介绍
由于个人电脑配置限制,本次评测的两个国产明星大模型都是32b版本,其中由于Deepseek-r1 没有专门的编程版本,所以采用通用版本,Qwen2.5采用Qwen2.5-coder编程版本。
- Deepseek-r1:32B: 模型大小20G,320亿参数,基础版,非蒸馏版本。
- Qwen-code:32B
第二部分:模型表现
Qweb2.5-coder:32b
首先上场的是Qweb2.5-coder:32b, 首先是一段任务分析:
然后就开始进入代码编写阶段了,token生成速度还可以,一秒10个左右:
编写扫雷游戏代码
index.html
网页版扫雷游戏
网页版扫雷游戏
<script src="script.js"></script>
styles.css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f4f4f9;
}
.container {
text-align: center;
}
#game-board {
display: grid;
gap: 2px;
width: fit-content;
margin: auto;
}
.cell {
width: 30px;
height: 30px;
background-color: #ddd;
border: 1px solid #ccc;
cursor: pointer;
font-size: 18px;
display: flex;
justify-content: center;
align-items: center;
}
.cell.revealed {
background-color: #fff;
border: 1px solid #999;
}
.cell.flagged {
background-image: url('flag.png');
background-size: cover;
}
#status {
margin-top: 20px;
font-weight: bold;
}
script.js
const gameBoard = document.getElementById('game-board');
const newGameBtn = document.getElementById('new-game-btn');
const statusDisplay = document.getElementById('status');
let boardSize = 10; // 游戏板大小(10x10)
let numMines = 20; // 地雷数量
let board = [];
let revealedCells = 0;
function createBoard() {
gameBoard.innerHTML = '';
board = Array.from({ length: boardSize }, () => Array(boardSize).fill(0));
revealedCells = 0;
statusDisplay.textContent = '';
placeMines();
calculateNumbers();
for (let i = 0; i < boardSize; i++) {
for (let j = 0; j < boardSize; j++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.dataset.row = i;
cell.dataset.col = j;
cell.addEventListener('click', handleCellClick);
cell.addEventListener('contextmenu', handleRightClick);
gameBoard.appendChild(cell);
}
}
}
function placeMines() {
let minesPlaced = 0;
while (minesPlaced < numMines) {
const row = Math.floor(Math.random() * boardSize);
const col = Math.floor(Math.random() * boardSize);
if (board[row][col] !== -1) {
board[row][col] = -1;
minesPlaced++;
}
}
}
function calculateNumbers() {
for (let i = 0; i < boardSize; i++) {
for (let j = 0; j < boardSize; j++) {
if (board[i][j] === -1) continue;
let mineCount = 0;
for (let x = Math.max(0, i - 1); x <= Math.min(boardSize - 1, i + 1); x++) {
for (let y = Math.max(0, j - 1); y <= Math.min(boardSize - 1, j + 1); y++) {
if (board[x][y] === -1) mineCount++;
}
}
board[i][j] = mineCount;
}
}
}
function handleCellClick(event) {
const row = parseInt(event.target.dataset.row);
const col = parseInt(event.target.dataset.col);
if (board[row][col] === -1) {
revealMines();
statusDisplay.textContent = '游戏结束!你踩到地雷了!';
} else {
revealCell(row, col);
if (revealedCells === boardSize * boardSize - numMines) {
statusDisplay.textContent = '恭喜,你赢了!';
}
}
}
function revealCell(row, col) {
const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
if (cell.classList.contains('revealed') || cell.classList.contains('flagged')) return;
cell.classList.add('revealed');
revealedCells++;
if (board[row][col] === 0) {
for (let x = Math.max(0, row - 1); x <= Math.min(boardSize - 1, row + 1); x++) {
for (let y = Math.max(0, col - 1); y <= Math.min(boardSize - 1, col + 1); y++) {
revealCell(x, y);
}
}
} else if (board[row][col] !== -1) {
cell.textContent = board[row][col];
}
}
function revealMines() {
for (let i = 0; i < boardSize; i++) {
for (let j = 0; j < boardSize; j++) {
const cell = document.querySelector(`.cell[data-row="${i}"][data-col="${j}"]`);
if (board[i][j] === -1) {
cell.classList.add('revealed');
cell.textContent = '';
}
}
}
}
function handleRightClick(event) {
event.preventDefault();
const row = parseInt(event.target.dataset.row);
const col = parseInt(event.target.dataset.col);
const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
if (cell.classList.contains('revealed')) return;
if (cell.classList.contains('flagged')) {
cell.classList.remove('flagged');
} else {
cell.classList.add('flagged');
}
}
newGameBtn.addEventListener('click', createBoard);
createBoard();
代码写完了还提供了一段说明:
还是看看成品吧:
纳尼,这样式好像有些问题啊?但是还是可以点击的,多点几下就踩雷了:
总结:代码完成读30分,可玩性30分,需要二次样式调整优化。
Qweb2.5-coder:32b
接着上场的是Deepseek-r1:32b, 首先是一段任务分析:
接着进入正式编码阶段:
网页版扫雷游戏源代码实现步骤说明
为了创建一个网页版的扫雷游戏,我们将按照以下步骤进行:
1. 创建基本的HTML结构
首先,我们需要定义一个HTML页面,其中包含游戏的主要元素:网格容器、控制按钮和难度选择器。
网页版扫雷
时间: 0 秒
剩余地雷: 0
<script src="script.js"></script>
2. 添加CSS样式
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
.game-container {
text-align: center;
background-color: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.grid-container {
margin: 10px auto;
}
.cell {
width: 40px;
height: 40px;
border: 1px solid #ccc;
display: inline-block;
background-color: #eee;
cursor: pointer;
}
.controls {
margin-bottom: 10px;
}
.info p {
margin: 5px 0;
}
3. 编写JavaScript核心逻辑
let grid = [];
let gridSize = { rows: 10, cols: 10 };
let mineCount = 10;
let gameOver = false;
let timer = null;
let firstClick = true;
// 初始化网格
function initGrid() {
const container = document.getElementById('gridContainer');
container.innerHTML = '';
grid = [];
for (let i = 0; i < gridSize.rows; i++) {
grid[i] = [];
const row = document.createElement('div');
for (let j = 0; j < gridSize.cols; j++) {
grid[i][j] = { isMine: false, isOpened: false, isFlagged: false };
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = i;
cell.dataset.col = j;
cell.addEventListener('click', handleClick);
row.appendChild(cell);
}
container.appendChild(row);
}
}
// 设置地雷
function setMines(firstRow, firstCol) {
let minesPlaced = 0;
while (minesPlaced < mineCount) {
const row = Math.floor(Math.random() * gridSize.rows);
const col = Math.floor(Math.random() * gridSize.cols);
if (!grid[row][col].isMine && !(row === firstRow && col === firstCol)) {
grid[row][col].isMine = true;
minesPlaced++;
}
}
}
// 计算相邻地雷数量
function getAdjacentMines(row, col) {
let count = 0;
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1 j const newrow='row' i const newcol='col' j if newrow>= 0 && newRow < gridsize.rows newcol>= 0 && newCol < gridSize.cols &&
grid[newRow][newCol].isMine) {
count++;
}
}
}
return count;
}
// 处理点击事件
function handleClick() {
if (gameOver || this.classList.contains('flagged')) return;
const row = parseInt(this.dataset.row);
const col = parseInt(this.dataset.col);
if (firstClick) {
firstClick = false;
setMines(row, col);
startTimer();
}
if (grid[row][col].isMine) {
gameOver = true;
revealAll();
alert('游戏结束!');
return;
}
const adjacentMines = getAdjacentMines(row, col);
if (adjacentMines === 0) {
floodFill(row, col);
} else {
grid[row][col].isOpened = true;
this.textContent = adjacentMines.toString();
checkWin();
}
}
// 洪水填充算法,递归打开相邻空格
function floodFill(row, col) {
if (row < 0 row>= gridSize.rows ||
col < 0 col>= gridSize.cols ||
grid[row][col].isOpened ||
grid[row][col].isMine) {
return;
}
const adjacentMines = getAdjacentMines(row, col);
grid[row][col].isOpened = true;
this.textContent = (adjacentMines > 0 ? adjacentMines.toString() : '');
if (adjacentMines === 0) {
// 递归检查相邻的八个格子
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1 j floodfillrow i col j checkwin function checkwin let remainingcells='0;' for let row of grid for let cell of row if cell.ismine cell.isopened remainingcells if remainingcells='== 0)' gameover='true;' clearintervaltimer alert function revealall for let row of grid for let cell of row if cell.ismine const element='document.querySelector(`[data-row="${row}"][data-col="${col}"]`);' element.textcontent function starttimer let seconds='0;' timer='setInterval(()'> {
document.getElementById('time').textContent = seconds++;
}, 1000);
}
// 新游戏
function newGame() {
gameOver = false;
firstClick = true;
clearInterval(timer);
initGrid();
}
// 设置难度
function setDifficulty(rows, cols, mines) {
gridSize.rows = rows;
gridSize.cols = cols;
mineCount = mines;
newGame();
}
// 初始化游戏
initGrid();
// 绑定事件监听器到难度按钮
document.querySelectorAll('.difficulty-btn').forEach(btn => {
btn.addEventListener('click', () => {
const difficulty = btn.dataset.difficulty;
switch(difficulty) {
case 'easy':
setDifficulty(10, 10, 10);
break;
case 'medium':
setDifficulty(20, 20, 40);
break;
case 'hard':
setDifficulty(30, 30, 90);
break;
}
});
});
// 绑定右键点击事件
document.addEventListener('contextmenu', (e) => {
if (!gameOver && e.target.classList.contains('cell')) {
e.preventDefault();
const cell = e.target;
if (!cell.classList.contains('flagged') && !cell.classList.contains('opened')) {
cell.classList.toggle('flagged');
updateMineCount();
}
}
});
// 更新剩余地雷数量
function updateMineCount() {
let flaggedMines = 0;
document.querySelectorAll('.cell').forEach(cell => {
if (cell.classList.contains('flagged')) flaggedMines++;
});
const mineDisplay = document.getElementById('mine-count');
mineDisplay.textContent = mineCount - flaggedMines;
}
// 初始化剩余地雷显示
updateMineCount();
### 回答
为了帮助你逐步解决这个问题,我将提供一个分步骤的解决方案。以下是详细的步骤说明:
### 步骤 1:理解需求
我们需要开发一个经典的“扫雷”游戏,该游戏需要以下功能:
- **开始新游戏**:点击按钮后,重置游戏状态。
- **难度选择**:支持三种不同的难度级别(简单、中等、困难)。
- **显示剩余地雷数量**:实时更新剩余未标记的地雷数量。
- **右键标记功能**:允许玩家用右键标记可能包含地雷的方块。
### 步骤 2:规划游戏逻辑
首先,我们需要明确扫雷游戏的基本规则和逻辑:
1. **网格初始化**:根据选择的难度级别,创建一个指定大小的网格。
2. **放置地雷**:在网格中随机放置一定数量的地雷。注意,在第一次点击时,不能将地雷放在被点击的方块及其相邻的方块内。
3. **揭开方块**:当玩家点击一个方块时,如果该方块是地雷,则游戏结束;否则,显示该方块周围地雷的数量。如果数量为零,则递归打开所有相邻的空白方块( floods fill 算法)。
4. **标记功能**:允许玩家用右键标记可能包含地雷的方块。
5. **胜利条件**:当所有非地雷方块都被正确揭开时,玩家获胜。
### 步骤 3:设计数据结构
为了实现上述逻辑,我们需要以下数据结构:
- **网格表示**:使用一个二维数组 `grid` 来存储每个方块的状态。每个元素是一个对象,包含以下属性:
- `isMine`:布尔值,指示该方块是否为地雷。
- `isOpened`:布尔值,指示该方块是否已被揭开。
- `isFlagged`:布尔值,指示该方块是否被标记。
### 步骤 4:实现网格初始化
在游戏开始或难度改变时,我们需要重新初始化网格。这包括:
1. 根据当前难度级别创建指定大小的网格。
2. 初始化所有方块的状态为默认状态(非地雷、未揭开、未标记)。
### 步骤 5:放置地雷
在第一次点击某个方块时,需要确保该方块及其相邻的方块内不放置地雷。这可以通过以下步骤实现:
1. 记录第一次点击的位置。
2. 随机在网格中选择位置放置地雷,避开第一次点击的位置及其周围的八个格子。
### 步骤 6:揭开方块逻辑
当玩家点击一个未标记且未揭开的方块时:
- 如果该方块是地雷,则游戏结束。
- 否则,计算该方块周围地雷的数量并显示。如果数量为零,则递归打开所有相邻的空白方块。
### 步骤 7:标记功能
允许玩家用右键点击方块来标记可能的地雷:
1. 在右键事件中,切换方块的 `isFlagged` 状态。
2. 更新剩余地雷计数器。
### 步骤 8:胜利和失败检测
- **失败条件**:当玩家点击一个地雷时,游戏结束,并显示所有地雷的位置。
- **胜利条件**:当所有非地雷方块都被正确揭开(即没有被标记或揭开的所有空白方块都已打开)。
### 步骤 9:用户界面设计
创建一个简单的 HTML 页面,包含:
1. 游戏网格区域。
2. 难度选择按钮。
3. 新游戏按钮。
4. 剩余地雷计数器。
### 步骤 10:实现 JavaScript 逻辑
编写 JavaScript 代码来控制游戏的逻辑,包括:
- 初始化网格。
- 处理玩家点击和右键事件。
- 更新剩余地雷计数器。
- 检测胜利或失败条件。
### 步骤 11:测试和调试
在完成上述步骤后,进行全面的测试以确保所有功能正常工作。特别注意以下方面:
- **第一次点击无地雷**:确认第一次点击的位置不会触发地雷。
- **标记功能**:确保右键标记正确更新剩余地雷计数器,并且无法揭开已被标记的方块。
- **胜利和失败条件**:测试各种情况,确保游戏能够正确检测到胜利或失败。
### 步骤 12:优化用户体验
根据测试反馈,进行以下优化:
- 添加视觉效果(如颜色变化)以增强用户反馈。
- 提供清晰的游戏状态提示(如“已获胜”或“已爆炸”)。
- 确保界面在不同难度级别下响应良好。
### 步骤 13:最终部署
将所有代码整合到一个完整的 HTML 文件中,并进行最后的测试,确保没有遗漏的功能或错误。然后,将该文件部署到服务器或共享位置,供用户访问和游玩。
通过以上步骤,你可以逐步开发出一个功能完善的扫雷游戏,满足基本的游戏规则和用户体验要求。
好了,我们来看看成品:
样式看的有模有样,玩一把看看,竟然还有计时器功能,只是点击之后的格子会掉下来,有一点点小样式问题。
多点击几个格子后,突然没反应了,打开调试代码窗口看看:
原来遇到了Javascript语法错误,。详细找了一下,出错的行是这里第六行,现实col没有定义错误。
// 显示所有地雷
function revealAll() {
for (let row of grid) {
for (let cell of row) {
if (cell.isMine) {
const element = document.querySelector(`[data-row="${row}"][data-col="${col}"]`);
element.textContent = '';
}
}
}
}
从头看了一下代码,发现这里有语法错误,row和cell都不是简单的数值类型,而是object类型,简单修改一下让他可以跑:
// 初始化网格
function initGrid() {
const container = document.getElementById('gridContainer');
container.innerHTML = '';
grid = [];
for (let i = 0; i < gridSize.rows; i++) {
grid[i] = [];
const row = document.createElement('div');
for (let j = 0; j < gridSize.cols; j++) {
// 代码手动修改部分,增加r和c两个字段分别表示cell所在的行和列序号
grid[i][j] = { isMine: false, isOpened: false, isFlagged: false, r: i, c:j };
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = i;
cell.dataset.col = j;
cell.addEventListener('click', handleClick);
row.appendChild(cell);
}
container.appendChild(row);
}
}
function revealAll() {
for (let row of grid) {
for (let cell of row) {
if (cell.isMine) {
// 代码手动修改部分,修改为从cell.r和cell.c 来找到对应的格子
const element = document.querySelector(`[data-row="${cell.r}"][data-col="${cell.c}"]`);
element.textContent = '';
}
}
}
}
修改后基本可以正常玩了。
总结:相比于Qwen2.5-coder,Deepseek-r1非常贴心的引入了游戏计时和游戏难度选择两个额外功能,是的游戏的可玩性大大增加,可以打90分;代码完成读方面,由于Bug的存在,只能打80分。
第三部分:模型评判
通过此次对Deepseek-r1和Qwen-2.5-coder在实现扫雷游戏中的对比分析,我们得出了几个值得注意的结论。首先,Deepseek-r1的表现大大超出了预期,在编程领域展现了不俗的实力。它不仅能够快速完成扫雷游戏的核心功能,还额外提供了“游戏计时”和“难度选择”等实用功能,极大地提升了用户体验。尽管在实现过程中出现了一些小的代码Bug,导致整体评分略有下降,但这些瑕不掩瑜,Deepseek-r1的整体表现令人印象深刻。
反观Qwen-2.5,作为一款专门针对编程任务的大模型,其表现却让人略感失望。在实现扫雷游戏的过程中,Qwen-2.5不仅缺少“计时功能”和“难度设置”的支持,甚至连基本的网页样式设计都显得不够完善,导致最终的游戏体验大打折扣。这一结果或许与我们对它的高期望形成了鲜明对比。
从这次实践来看,Deepseek-r1作为一款通用型AI模型,在编程领域的表现已经足够令人惊艳。它不仅能够完成基础功能开发,还能在一定程度上满足用户对复杂需求的期待。而Qwen-2.5虽然专注于编程任务,但在实际应用中却显得力不从心,其短板暴露了当前AI编程工具在功能完善性和用户体验上的不足。
总的来说,随着AI技术的不断进步,未来像Deepseek-r1这样的通用型模型可能会在更多领域展现出更强的适应能力。而对于Qwen-2.5这类专业编程模型来说,如何提升实用性、优化功能设计,将是其未来发展的重要方向。无论选择哪种工具,开发者都需要根据自己的需求和场景进行权衡,才能最大化AI带来的价值。
希望这篇文章能为开发者提供一些参考,在选择AI编程工具时更加明智,同时也期待未来能看到更多优秀的AI工具诞生,为软件开发注入新的活力!