admin 管理员组

文章数量: 1184232

本文介绍了如何使用 Amazon Q 工具对俄罗斯方块游戏代码进行 Bug 修复与优化。首先,阐述了 Amazon Q 的主要功能,如代码分析、自动重构和单元测试生成,并说明其在项目中的应用。接着,详细描述了实验环境和过程,包括项目分析、功能重构、单元测试生成与应用,以及优化效果的验证。Amazon Q 在此过程中展现了其在自动化代码修复与性能优化方面的强大能力,显著提高了代码的稳定性和开发效率。


一、实验背景介绍

1. 介绍 Amazon Q 的基本能力与新特性

Amazon Q 的基本能力与新特性如下:

核心能力

功能详细描述
代码分析与审查深入分析代码结构,识别潜在的错误、漏洞和性能瓶颈。
自动化重构提供智能的代码重构建议,优化代码可读性、可维护性和执行效率。
单元测试生成自动生成覆盖全面的单元测试,确保代码质量和功能正确性。
实时协助开发过程中提供实时的代码建议和改进措施,提高开发效率。

新特性

新特性详细描述
深度学习集成利用深度学习算法,提升代码理解和优化能力。
多语言支持支持多种编程语言(如 JavaScript、Python、Java 等)。
集成开发环境(IDE)插件与主流 IDE(如 VS Code 和 IntelliJ IDEA)无缝集成,提供实时反馈。

2. Amazon Q 在项目中的作用

在项目中,Amazon Q 可以在以下几个方面提供帮助:

作用详细描述
代码质量提升自动化审查和重构建议,提升代码质量和维护性。
错误检测与修复快速识别并修复潜在的代码错误和漏洞,减少开发过程中的错误。
性能优化识别性能瓶颈并提供优化建议,提升应用程序的执行效率。
测试覆盖自动生成单元测试,确保代码功能的全面覆盖和正确性。
文档生成自动生成准确的项目文档,帮助团队理解和维护代码。

3. 实验目标

本实验的目标是使用 Amazon Q 对现有的俄罗斯方块游戏代码进行优化,具体目标如下:

  • 修复 bug:解决游戏在方块堆积到顶端时未能结束的问题。
  • 代码优化:提升代码可读性、可维护性与执行效率。
  • 单元测试覆盖:生成并应用单元测试,确保代码的正确性和功能。
  • 功能验证:通过运行项目来验证优化效果,确保游戏功能正常且性能提升。

二、实验环境说明

本次实验环境如下:

  • 开发框架:Vue.js 3
  • 编程语言:JavaScript (ES6+)
  • 开发工具:Visual Studio Code
  • 依赖管理:npm
  • 操作系统:Windows 10 / macOS
  • Amazon Q 版本:最新版本(截至 2024-04)
  • 浏览器:Google Chrome 最新版

在实验开始之前,我们需要将现有的俄罗斯方块项目导入到 Visual Studio Code 中,提前安装好 Amazon Q 插件。


三、实验过程

1. 利用 Amazon Q 编写项目文档

接下来,使用 Amazon Q 生成项目文档,只需输入 /doc,选择提示中的选项,或者直接键入 Create a READMEAmazon Q 会自动扫描整个项目,分析项目结构与功能,在了解项目整体全貌的基础上,智能地生成一份完整的 README.md 文档,帮助开发者快速创建详细的项目说明。


Amazon Q 会询问你是否接受这个文件,我们可以点击 README.md 查看文件内容。


如果文件内容合适,你可以选择 Accept 这个文件,这个文件会被自动加入到项目里面。


2. 利用 Amazon Q 解释代码

选中代码,通过 Ctrl + Shift + P 调用命令窗口,输入 Amazon Q: Explain ,或者直接按快捷键 Alt + Windows + E

左侧出现 Amazon Q 的窗口,显示代码解释的内容:

大致意思如下:

这段代码使用 Vue.js 实现了一个俄罗斯方块游戏,采用 20x10 的网格。游戏包含七种不同的方块,每种方块有独特的颜色,并记录玩家的分数。玩家可以通过键盘控制方块的左移、右移、下落或旋转。游戏会生成新方块,检查碰撞,清除已完成的行,每行清除后会奖励100分。Vue 的响应式系统实时更新游戏界面。游戏自动让方块下落,直到新方块无法生成时结束。显示效果将下落的方块与固定方块合并,提供动态的游戏体验。

可以看到 Amazon Q 清晰的解释起了我们俄罗斯方块项目代码实现的功能和玩法。


3. 基于 Amazon Q 对功能 BUG 修复

这个项目我们运行起来,发现有一个 BUG,当方块堆积到顶端时,游戏未能正确结束。

这可能是由于 spawnTetromino 函数中的碰撞检测逻辑未能准确识别顶端堆积的情况。

我们使用 Amazon Q 来修复这个问题:

在左侧对话框力我们输入 /dev ,然后在新页面输入我们的问题:

Found a bug and the game failed to end correctly when the square
piled up to the top.

Amazon Q 会帮我们去分析项目代码,并提供修改方案。

本次返回如下:

Amazon Q 帮我们修改了 App.vue 文件,通过点击对话框里的 App.vue,我们可以看到 Amazon Q 帮我们修改的代码详情。

对话框里,Amazon Q 会问我们是否愿意接受这个改变,如果我们点击接受,我们的代码将被更新。
我们点击接受,然后运行起来看看 BUG 是否已被修复。

可以看到我们游戏结束条件的 BUG 已经被修复了。


4. 基于 Amazon Q 对项目代码优化

接下来,我们通过 Amazon Q 对代码进行全面优化,涵盖代码性能、可读性、注释补充和前端展示样式的提升。

① 代码性能优化

性能优化的目标是提高代码的执行效率,减少冗余计算,避免重复的循环或请求,并合理使用缓存来减轻服务器负担。我们利用 Amazon Q 的性能优化功能,精细化调整代码,以达到更高效的执行。

首先,打开代码文件并选中需要优化的部分。然后,右键点击并选择 Amazon Q 中的 Optimize(优化)选项,或直接使用快捷键 Alt + Win + AAmazon Q 会分析该部分代码,识别潜在的性能瓶颈,并提供智能优化建议,帮助开发者减少不必要的计算,提升代码执行速度。

Amazon Q 会在修改建议中提供代码示例来帮助我们实现性能优化。

以下是我实验中优化的代码部分:

// 优化前
// 计算合并后的网格,包括当前下落的方块
    const combinedGrid = computed(() => {
      const tempGrid = grid.value.map(row => row.slice()); // 深拷贝
      currentTetromino.shape.forEach((row, y) => {
        row.forEach((value, x) => {
          if (value) {
            const gridX = currentTetromino.x + x;
            const gridY = currentTetromino.y + y;
            if (gridY >= 0 && gridY < ROWS && gridX >= 0 && gridX < COLS) {
              // 如果当前网格位置没有固定的方块
              if (tempGrid[gridY][gridX] === 0) {
                tempGrid[gridY][gridX] = currentTetromino.color;
              }
            }
          }
        });
      });
      return tempGrid;
    });
    
// 优化后 - 算法上降低了时间复杂度和空间复杂性
const combinedGrid = computed(() => {
  // Create a new array and copy existing grid in one operation
  const tempGrid = Array.from({ length: ROWS }, (_, i) => [...grid.value[i]]);
  
  // Get current tetromino properties once
  const { shape, x: tetrominoX, y: tetrominoY, color } = currentTetromino;
  
  // Calculate bounds to minimize iteration space
  const shapeHeight = shape.length;
  const shapeWidth = shape[0].length;
  const maxY = Math.min(ROWS, tetrominoY + shapeHeight);
  const maxX = Math.min(COLS, tetrominoX + shapeWidth);
  
  // Only iterate over the area where the tetromino exists
  for (let y = Math.max(0, tetrominoY); y < maxY; y++) {
    for (let x = Math.max(0, tetrominoX); x < maxX; x++) {
      const shapeY = y - tetrominoY;
      const shapeX = x - tetrominoX;
      
      if (shape[shapeY][shapeX] && tempGrid[y][x] === 0) {
        tempGrid[y][x] = color;
      }
    }
  }
  
  return tempGrid;
});

通过这些优化,我们能够确保系统更加高效运行,降低卡顿和页面不稳定的情况发生的可能性。


② 代码可读性优化

原有的项目代码在功能实现的逻辑上并不完美,我们借助 Amazon QRefactor(重构)功能对代码进行优化。

首先,选中需要修改的代码部分,右键点击并选择 Amazon Q 中的 Refactor 按钮,或者直接使用快捷键 Alt + Win + U。此时,左侧的对话框会显示针对代码的修改建议,包括潜在的改进点和优化方案。Amazon Q 会根据其智能分析,提供代码重构的具体建议,帮助开发者提高代码的可维护性和可读性。

Amazon Q 对这段代码的重构改进包括单一职责、提升可读性、优化组织结构、简化状态管理、使用模板字面量、分离清理操作及保持不变性。增强了可维护性,便于测试,功能明确,逻辑清晰,代码更有条理,便于未来修改。

除了两种快捷键操作的代码优化方法之外,我们也可以使用 Ctrl + I,然后在上方命令框里输入你的需求:

比如我输入 optimize these code,回车之后,代码逐渐生成,红色区域的代码为当前的版本,绿色区域代码为 Amazon Q 修改后的版本,我可以接受或者拒绝这段修改。


③ 代码注释补充

良好的注释能够提高代码的可读性,方便团队成员理解和维护代码。
在需要代码注释的地方,Amazon Q 会为我们生成类似如下的注释模板:

/**
 * 检测给定形状在指定位置是否发生碰撞
 * 
 * @param {number} x - 形状的X轴位置
 * @param {number} y - 形状的Y轴位置
 * @param {Array<Array<boolean>>} shape - 形状的二维数组表示
 * @returns {boolean} 如果发生碰撞返回true,否则返回false
 */
const collision = (x, y, shape) => {
  // 遍历形状的每一行
  for (let row = 0; row < shape.length; row++) {
    // 遍历当前行的每一列
    for (let col = 0; col < shape[row].length; col++) {
      // 如果当前单元格是形状的一部分
      if (shape[row][col]) {
        // 计算形状在当前单元格的实际X轴位置
        const newX = x + col;
        // 计算形状在当前单元格的实际Y轴位置
        const newY = y + row;
        // 如果新的X轴位置超出边界或新的Y轴位置超出边界或当前位置已有元素存在
        if (
          newX < 0 ||
          newX >= COLS ||
          newY >= ROWS ||
          (newY >= 0 && grid.value[newY][newX])
        ) {
          // 则表示发生碰撞,返回true
          return true;
        }
      }
    }
  }
  // 如果所有单元格都没有发生碰撞,返回false
  return false;
};

这样,我们不仅优化了代码的性能,也确保代码更具可维护性和可理解性。


④ 前端展现样式优化

前端样式优化旨在提升用户体验,使页面更加美观和流畅。

我们通过 Amazon QOptimize(优化)功能,改善界面的响应式设计、配色方案、动画效果等。

Amazon Q 帮我们修改了 App.vuestyle.css 文件,并帮我们去掉了与项目无关的多余文件。

修改后,我们再次运行项目,效果如图:

App.vue 的全部代码:

<template>
  <div class="game-container">
    <h1 class="title">俄罗斯方块</h1>
    <div class="score-board">
      <div class="score">得分: {{ score }}</div>
      <div class="instructions">
        <p>使用箭头键移动方块</p>
        <p>上箭头旋转,↓加速下落</p>
      </div>
    </div>
    <div class="game-grid">
      <div
        v-for="(row, y) in combinedGrid"
        :key="y"
        class="grid-row"
      >
        <div
          v-for="(cell, x) in row"
          :key="x"
          class="grid-cell"
          :class="getCellClass(cell)"
        ></div>
      </div>
    </div>
    <div v-if="gameOver" class="game-over">
      <h2>游戏结束!得分: {{ score }}</h2>
      <button @click="restartGame">重新开始</button>
    </div>
  </div>
</template>

<script>
import { ref, onMounted, onUnmounted, reactive, computed } from 'vue';

export default {
  setup() {
    const ROWS = 20;
    const COLS = 10;
    const grid = ref(Array.from({ length: ROWS }, () => Array(COLS).fill(0)));
    const score = ref(0);
    const gameOver = ref(false);

    const tetrominoes = [
      // I
      [
        [0, 0, 0, 0],
        [1, 1, 1, 1],
        [0, 0, 0, 0],
        [0, 0, 0, 0],
      ],
      // J
      [
        [2, 0, 0],
        [2, 2, 2],
        [0, 0, 0],
      ],
      // L
      [
        [0, 0, 3],
        [3, 3, 3],
        [0, 0, 0],
      ],
      // O
      [
        [4, 4],
        [4, 4],
      ],
      // S
      [
        [0, 5, 5],
        [5, 5, 0],
        [0, 0, 0],
      ],
      // T
      [
        [0, 6, 0],
        [6, 6, 6],
        [0, 0, 0],
      ],
      // Z
      [
        [7, 7, 0],
        [0, 7, 7],
        [0, 0, 0],
      ],
    ];

    const colors = {
      0: '#111', // 空白
      1: '#00f0f0', // I - 青色
      2: '#0000f0', // J - 蓝色
      3: '#f0a000', // L - 橙色
      4: '#f0f000', // O - 黄色
      5: '#00f000', // S - 绿色
      6: '#a000f0', // T - 紫色
      7: '#f00000', // Z - 红色
    };

    const currentTetromino = reactive({
      shape: [],
      x: 0,
      y: 0,
      color: 0,
    });

    let dropInterval = null;

    const rotate = (matrix) => {
      return matrix[0].map((_, index) =>
        matrix.map((row) => row[index]).reverse()
      );
    };

    const collision = (x, y, shape) => {
      for (let row = 0; row < shape.length; row++) {
        for (let col = 0; col < shape[row].length; col++) {
          if (shape[row][col]) {
            const newX = x + col;
            const newY = y + row;
            if (
              newX < 0 ||
              newX >= COLS ||
              newY >= ROWS ||
              (newY >= 0 && grid.value[newY][newX])
            ) {
              return true;
            }
          }
        }
      }
      return false;
    };

    const merge = () => {
      currentTetromino.shape.forEach((row, y) => {
        row.forEach((value, x) => {
          if (value) {
            const gridX = currentTetromino.x + x;
            const gridY = currentTetromino.y + y;
            if (gridY >= 0 && gridX >= 0 && gridX < COLS && gridY < ROWS) {
              grid.value[gridY][gridX] = currentTetromino.color;
            }
          }
        });
      });
    };

    const clearLines = () => {
      let lines = 0;
      for (let y = ROWS - 1; y >= 0; y--) {
        if (grid.value[y].every(cell => cell !== 0)) {
          grid.value.splice(y, 1);
          grid.value.unshift(Array(COLS).fill(0));
          lines++;
          y++; // 重新检查当前行
        }
      }
      if (lines > 0) {
        score.value += lines * 100;
      }
    };

    const spawnTetromino = () => {
      const index = Math.floor(Math.random() * tetrominoes.length);
      const shape = tetrominoes[index];
      currentTetromino.shape = shape;
      currentTetromino.x = Math.floor((COLS - shape[0].length) / 2);
      currentTetromino.y = 0; // 将 y 设置为 0
      currentTetromino.color = index + 1;
      
      if (collision(currentTetromino.x, currentTetromino.y, currentTetromino.shape)) {
        gameOver.value = true;
        clearInterval(dropInterval);
        window.removeEventListener('keydown', handleKeyDown);
      }
    };

    const resetGame = () => {
      grid.value = Array.from({ length: ROWS }, () => Array(COLS).fill(0));
      score.value = 0;
      gameOver.value = false;
      spawnTetromino();
      dropInterval = setInterval(gameLoop, 1000);
      window.addEventListener('keydown', handleKeyDown);
    };

    const move = (dir) => {
      if (gameOver.value) return; // 游戏结束时禁止移动
      const newX = currentTetromino.x + dir;
      if (!collision(newX, currentTetromino.y, currentTetromino.shape)) {
        currentTetromino.x = newX;
      }
    };

    const drop = () => {
      if (gameOver.value) return; // 游戏结束时禁止下落
      const newY = currentTetromino.y + 1;
      if (!collision(currentTetromino.x, newY, currentTetromino.shape)) {
        currentTetromino.y = newY;
      } else {
        merge();
        clearLines();
        spawnTetromino();
      }
    };

    const rotateTetromino = () => {
      if (gameOver.value) return; // 游戏结束时禁止旋转
      const rotated = rotate(currentTetromino.shape);
      if (!collision(currentTetromino.x, currentTetromino.y, rotated)) {
        currentTetromino.shape = rotated;
      }
    };

    const gameLoop = () => {
      drop();
    };

    const handleKeyDown = (e) => {
      switch (e.key) {
        case 'ArrowLeft':
          move(-1);
          break;
        case 'ArrowRight':
          move(1);
          break;
        case 'ArrowDown':
          drop();
          break;
        case 'ArrowUp':
          rotateTetromino();
          break;
        default:
          break;
      }
    };

    const restartGame = () => {
      resetGame();
    };

    onMounted(() => {
      spawnTetromino();
      dropInterval = setInterval(gameLoop, 1000);
      window.addEventListener('keydown', handleKeyDown);
    });

    onUnmounted(() => {
      clearInterval(dropInterval);
      window.removeEventListener('keydown', handleKeyDown);
    });

    // 计算合并后的网格,包括当前下落的方块
    const combinedGrid = computed(() => {
      const tempGrid = grid.value.map(row => row.slice()); // 深拷贝
      currentTetromino.shape.forEach((row, y) => {
        row.forEach((value, x) => {
          if (value) {
            const gridX = currentTetromino.x + x;
            const gridY = currentTetromino.y + y;
            if (gridY >= 0 && gridY < ROWS && gridX >= 0 && gridX < COLS) {
              // 如果当前网格位置没有固定的方块
              if (tempGrid[gridY][gridX] === 0) {
                tempGrid[gridY][gridX] = currentTetromino.color;
              }
            }
          }
        });
      });
      return tempGrid;
    });

    const getCellClass = (cell) => {
      return {
        filled: cell !== 0,
        [`color-${cell}`]: cell !== 0,
      };
    };

    return {
      combinedGrid,
      score,
      getCellClass,
      gameOver,
      restartGame,
    };
  },
};
</script>

<style scoped>
/* 全局样式 */
body {
  margin: 0;
  padding: 0;
  background-color: #1e1e1e;
  color: #fff;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

/* 容器样式 */
.game-container {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;
  min-height: 100vh;
  background: linear-gradient(135deg, #2c3e50, #4ca1af);
}

/* 标题样式 */
.title {
  font-size: 3em;
  margin-bottom: 10px;
  color: #fff;
  text-shadow: 2px 2px 4px #000;
}

/* 计分板样式 */
.score-board {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-bottom: 20px;
}

.score {
  color: #fff;
  font-size: 1.5em;
  margin-bottom: 10px;
  padding: 10px 20px;
  background-color: rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}

.instructions {
  font-size: 0.9em;
  color: #ddd;
  text-align: center;
}

/* 游戏网格样式 */
.game-grid {
  display: grid;
  grid-template-rows: repeat(20, 30px);
  grid-template-columns: repeat(10, 30px);
  gap: 2px;
  background-color: #333;
  padding: 5px;
  border-radius: 10px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
  position: relative;
}

/* 行样式 */
.grid-row {
  display: contents;
}

/* 单元格样式 */
.grid-cell {
  width: 30px;
  height: 30px;
  background-color: #111;
  transition: background-color 0.2s, transform 0.1s;
  border-radius: 4px;
}

/* 方块填充样式 */
.grid-cell.filled {
  /* 默认填充颜色 */
  background-color: #0f0;
  animation: fadeIn 0.3s ease-in-out;
}

/* 不同颜色的方块 */
.color-1 {
  background-color: #00f0f0; /* I - 青色 */
}
.color-2 {
  background-color: #0000f0; /* J - 蓝色 */
}
.color-3 {
  background-color: #f0a000; /* L - 橙色 */
}
.color-4 {
  background-color: #f0f000; /* O - 黄色 */
}
.color-5 {
  background-color: #00f000; /* S - 绿色 */
}
.color-6 {
  background-color: #a000f0; /* T - 紫色 */
}
.color-7 {
  background-color: #f00000; /* Z - 红色 */
}

/* 分数与说明样式 */
.score {
  font-size: 1.5em;
  margin-bottom: 10px;
  padding: 10px 20px;
  background-color: rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}

.instructions {
  font-size: 0.9em;
  color: #ddd;
  text-align: center;
}

/* 游戏结束覆盖层样式 */
.game-over {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background-color: rgba(0, 0, 0, 0.85);
  padding: 30px 40px;
  border-radius: 15px;
  color: #fff;
  text-align: center;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.7);
  animation: fadeInOverlay 0.5s ease-in-out;
}

.game-over h2 {
  margin-bottom: 20px;
  font-size: 2em;
  text-shadow: 1px 1px 3px #000;
}

.game-over button {
  padding: 12px 24px;
  background-color: #00f0f0;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-size: 1em;
  transition: background-color 0.3s, transform 0.2s;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}

.game-over button:hover {
  background-color: #00c0c0;
  transform: translateY(-2px);
}

/* 动画效果 */
@keyframes fadeIn {
  from {
    opacity: 0;
    transform: scale(0.95);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes fadeInOverlay {
  from {
    opacity: 0;
    transform: translate(-50%, -60%);
  }
  to {
    opacity: 1;
    transform: translate(-50%, -50%);
  }
}

/* 响应式设计 */
@media (max-width: 600px) {
  .game-container {
    padding: 10px;
  }

  .title {
    font-size: 2em;
  }

  .game-grid {
    grid-template-rows: repeat(20, 20px);
    grid-template-columns: repeat(10, 20px);
  }

  .grid-cell {
    width: 20px;
    height: 20px;
  }

  .score {
    font-size: 1.2em;
    padding: 8px 16px;
  }

  .game-over {
    padding: 20px 30px;
  }

  .game-over h2 {
    font-size: 1.5em;
  }

  .game-over button {
    padding: 10px 20px;
    font-size: 0.9em;
  }
}
</style>


四、结论

通过本次实验,利用 Amazon Q 对俄罗斯方块项目进行了全面的分析、Bug 修复和代码优化。Amazon Q 展示了其在代码审查、重构建议、单元测试生成等方面的强大能力,显著提升了项目的质量和开发效率。特别是在解决游戏结束逻辑 Bug 和优化关键函数方面,Amazon Q 提供了精准且高效的解决方案,确保了代码的稳定性与性能。

未来,Amazon Q 有望在更多项目中发挥关键作用,助力开发团队构建高质量的软件产品,进一步提升开发过程的智能化与自动化水平。

本文标签: 俄罗斯方块 代码 Amazon bug