-
-
bot昵称: {{bot_name}}
- bot战力: {{bot_rating}}
-
-
+
+
diff --git a/web/src/assets/background.png b/web/src/assets/images/background.png
similarity index 100%
rename from web/src/assets/background.png
rename to web/src/assets/images/background.png
diff --git a/web/src/assets/scripts/AcGameObject.js b/web/src/assets/scripts/AcGameObject.js
new file mode 100644
index 0000000..8887af0
--- /dev/null
+++ b/web/src/assets/scripts/AcGameObject.js
@@ -0,0 +1,50 @@
+const AC_GAME_OBJECTS = [];
+
+export class AcGameObject {
+ constructor() {
+ AC_GAME_OBJECTS.push(this); // 数组添加元素
+ this.timedelta = 0;
+ this.has_called_start = false;
+ }
+
+ start() { // 只执行一次
+ }
+
+ update() { // 每一帧执行一次,除了第一帧之外
+
+ }
+
+ on_destroy() { // 删除之前执行
+
+ }
+
+ destroy() {
+ this.on_destroy();
+
+ for (let i in AC_GAME_OBJECTS) { // 遍历数组下标
+ const obj = AC_GAME_OBJECTS[i];
+ if (obj === this) { // 注意是三个等号
+ AC_GAME_OBJECTS.splice(i); // 从数组中移除元素
+ break;
+ }
+ }
+ }
+}
+
+let last_timestamp; // 上一次执行的时刻
+const step = timestamp => {
+ for (let obj of AC_GAME_OBJECTS) { // 遍历数组的值
+ if (!obj.has_called_start) { // 如果对象没有被调用
+ obj.has_called_start = true;
+ obj.start();
+ } else {
+ obj.timedelta = timestamp - last_timestamp; // 获得当前帧和上一帧的时间差
+ obj.update();
+ }
+ }
+
+ last_timestamp = timestamp; // 将当前时间更新为上一帧执行时刻
+ requestAnimationFrame(step) // 递归调用该函数
+}
+
+requestAnimationFrame(step)
\ No newline at end of file
diff --git a/web/src/assets/scripts/GameMap.js b/web/src/assets/scripts/GameMap.js
new file mode 100644
index 0000000..439fb1d
--- /dev/null
+++ b/web/src/assets/scripts/GameMap.js
@@ -0,0 +1,116 @@
+import { AcGameObject } from "./AcGameObject"; // 导入js的export class
+import { Wall } from "./Wall";
+
+export class GameMap extends AcGameObject {
+ constructor(ctx, parent) {
+ super(); // 继承类一直要先调用父类的构造函数
+
+ this.ctx = ctx;
+ this.parent = parent;
+ this.L = 0;
+
+ this.rows = 13;
+ this.cols = 13;
+
+ this.inner_walls_count = 20; // 障碍物的数量(最大建议80)
+ this.walls = []; // 所有障碍物组成的数组
+ }
+
+ check_connectivity(g, sx, sy, tx, ty) { // 判断生成的地图是否可以连通
+ if (sx == tx && sy == ty) return true;
+ g[sx][sy] = true;
+
+ let dx = [-1, 0, 1, 0], dy = [0, 1, 0, -1];
+ for (let i = 0; i < 4; i ++ ) {
+ let x = sx + dx[i], y = sy + dy[i];
+ if (!g[x][y] && this.check_connectivity(g, x, y, tx, ty))
+ return true;
+ }
+
+ return false;
+ }
+
+ create_walls() { // 判断是否生成有效的地图
+ const g = [];
+ for (let r = 0; r < this.rows; r ++ ) {
+ g[r] = [];
+ for (let c = 0; c < this.cols; c ++ ) {
+ g[r][c] = false;
+ }
+ }
+
+ // 给四周加上障碍物
+ for (let r = 0; r < this.rows; r ++ ) {
+ g[r][0] = g[r][this.cols - 1] = true;
+ }
+
+ for (let c = 0; c < this.cols; c ++ ) {
+ g[0][c] = g[this.rows - 1][c] = true;
+ }
+
+ // 创建随机障碍物
+ for (let i = 0; i < this.inner_walls_count / 2; i ++ ) {
+ for (let j = 0; j < 1000; j ++ ) {
+ let r = parseInt(Math.random() * this.rows);
+ let c = parseInt(Math.random() * this.cols);
+ if (g[r][c] || g[c][r]) continue;
+ // 不能将障碍物生成到两条蛇的起始位置处(左下,右上)
+ if (r == this.rows - 2 && c == 1 || r == 1 && c == this.cols - 2)
+ continue;
+ // 每次将障碍物生成到对称的两个位置
+ g[r][c] = g[c][r] = true;
+ break;
+ }
+ }
+
+ const copy_g = JSON.parse(JSON.stringify(g));
+
+ // 如果生成的地图无法连通,则返回false,需要重新生成
+ if (!this.check_connectivity(copy_g, this.rows - 2, 1, 1, this.cols - 2))
+ return false;
+
+ for (let r = 0; r < this.rows; r ++ ) {
+ for (let c = 0; c < this.cols; c ++ ) {
+ if (g[r][c]) {
+ // 每次添加一个障碍物
+ this.walls.push(new Wall(r, c, this));
+ }
+ }
+ }
+
+ return true;
+ }
+
+ start() {
+ // 尝试1000次,直到找到符合条件的地图为止
+ for (let i = 0; i < 1000; i ++ )
+ if (this.create_walls())
+ break;
+ }
+
+ update_size() { // 每过一帧重新生成新的地图尺寸
+ // 取整数
+ this.L = parseInt(Math.min(this.parent.clientWidth / this.cols, this.parent.clientHeight / this.rows));
+ this.ctx.canvas.width = this.L * this.cols;
+ this.ctx.canvas.height = this.L * this.rows;
+ }
+
+ update() { // 更新地图:每隔一帧都要重新渲染
+ this.update_size(); // 更新地图大小
+ this.render(); // 重新渲染
+ }
+
+ render() {
+ const color_even = "#AAD751", color_odd = "#A2D149";
+ for (let r = 0; r < this.rows; r ++ ) {
+ for (let c = 0; c < this.cols; c ++ ) {
+ if ((r + c) % 2 == 0) {
+ this.ctx.fillStyle = color_even;
+ } else {
+ this.ctx.fillStyle = color_odd;
+ }
+ this.ctx.fillRect(c * this.L, r * this.L, this.L, this.L);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/web/src/assets/scripts/Wall.js b/web/src/assets/scripts/Wall.js
new file mode 100644
index 0000000..f3b0096
--- /dev/null
+++ b/web/src/assets/scripts/Wall.js
@@ -0,0 +1,24 @@
+import { AcGameObject } from "./AcGameObject";
+
+export class Wall extends AcGameObject {
+ constructor(r, c, gamemap) {
+ super();
+
+ this.r = r;
+ this.c = c;
+ this.gamemap = gamemap;
+ this.color = "#B37226"; // 障碍物的颜色
+ }
+
+ update() {
+ this.render();
+ }
+
+ render() {
+ const L = this.gamemap.L;
+ const ctx = this.gamemap.ctx;
+
+ ctx.fillStyle = this.color;
+ ctx.fillRect(this.c * L, this.r * L, L, L); // 将对应位置填充为障碍物
+ }
+}
\ No newline at end of file
diff --git a/web/src/components/ContentField.vue b/web/src/components/ContentField.vue
new file mode 100644
index 0000000..d302e7d
--- /dev/null
+++ b/web/src/components/ContentField.vue
@@ -0,0 +1,18 @@
+