diff --git a/web/jsconfig.json b/web/jsconfig.json index 4aafc5f..f1bc575 100644 --- a/web/jsconfig.json +++ b/web/jsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es6", "module": "esnext", "baseUrl": "./", "moduleResolution": "node", diff --git a/web/package-lock.json b/web/package-lock.json index 4befc48..344a8ba 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1928,6 +1928,12 @@ "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", "dev": true }, + "node_modules/@popperjs/core": { + "version": "2.11.5", + "resolved": "https://registry.npmmirror.com/@popperjs/core/-/core-2.11.5.tgz", + "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==", + "peer": true + }, "node_modules/@sideway/address": { "version": "4.1.4", "resolved": "https://registry.npmmirror.com/@sideway/address/-/address-4.1.4.tgz", @@ -12219,6 +12225,12 @@ "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", "dev": true }, + "@popperjs/core": { + "version": "2.11.5", + "resolved": "https://registry.npmmirror.com/@popperjs/core/-/core-2.11.5.tgz", + "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==", + "peer": true + }, "@sideway/address": { "version": "4.1.4", "resolved": "https://registry.npmmirror.com/@sideway/address/-/address-4.1.4.tgz", @@ -12739,7 +12751,8 @@ "version": "5.0.8", "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.8.tgz", "integrity": "sha512-HSYWPqrunRE5ZZs8kVwiY6oWcn95qf/OQabwLfprhdpFWAGtLStShjsGED2aDpSSeGAskQETrtR/5h7VqgIlBA==", - "dev": true + "dev": true, + "requires": {} }, "@vue/cli-service": { "version": "5.0.8", @@ -13250,13 +13263,15 @@ "version": "1.8.0", "resolved": "https://registry.npmmirror.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true + "dev": true, + "requires": {} }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "acorn-walk": { "version": "8.2.0", @@ -13315,7 +13330,8 @@ "version": "3.5.2", "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "ansi-colors": { "version": "4.1.3", @@ -13603,7 +13619,8 @@ "bootstrap": { "version": "5.1.3", "resolved": "https://registry.npmmirror.com/bootstrap/-/bootstrap-5.1.3.tgz", - "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==" + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==", + "requires": {} }, "brace-expansion": { "version": "1.1.11", @@ -14131,7 +14148,8 @@ "version": "6.3.0", "resolved": "https://registry.npmmirror.com/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz", "integrity": "sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og==", - "dev": true + "dev": true, + "requires": {} }, "css-loader": { "version": "6.7.1", @@ -14302,7 +14320,8 @@ "version": "3.1.0", "resolved": "https://registry.npmmirror.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz", "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", - "dev": true + "dev": true, + "requires": {} }, "csso": { "version": "4.2.0", @@ -15758,7 +15777,8 @@ "version": "5.1.0", "resolved": "https://registry.npmmirror.com/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true + "dev": true, + "requires": {} }, "ieee754": { "version": "1.2.1", @@ -17130,25 +17150,29 @@ "version": "5.1.2", "resolved": "https://registry.npmmirror.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-discard-duplicates": { "version": "5.1.0", "resolved": "https://registry.npmmirror.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-discard-empty": { "version": "5.1.1", "resolved": "https://registry.npmmirror.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", - "dev": true + "dev": true, + "requires": {} }, "postcss-discard-overridden": { "version": "5.1.0", "resolved": "https://registry.npmmirror.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-loader": { "version": "6.2.1", @@ -17238,7 +17262,8 @@ "version": "3.0.0", "resolved": "https://registry.npmmirror.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -17273,7 +17298,8 @@ "version": "5.1.0", "resolved": "https://registry.npmmirror.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", - "dev": true + "dev": true, + "requires": {} }, "postcss-normalize-display-values": { "version": "5.1.0", @@ -19110,7 +19136,8 @@ "version": "8.8.0", "resolved": "https://registry.npmmirror.com/ws/-/ws-8.8.0.tgz", "integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==", - "dev": true + "dev": true, + "requires": {} } } }, @@ -19237,7 +19264,8 @@ "version": "7.5.8", "resolved": "https://registry.npmmirror.com/ws/-/ws-7.5.8.tgz", "integrity": "sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==", - "dev": true + "dev": true, + "requires": {} }, "y18n": { "version": "5.0.8", diff --git a/web/public/favicon.ico b/web/public/favicon.ico index df36fcf..6432bdc 100644 Binary files a/web/public/favicon.ico and b/web/public/favicon.ico differ diff --git a/web/src/App.vue b/web/src/App.vue index dad5c1c..f47542d 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -1,39 +1,23 @@ 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 @@ + + + + + \ No newline at end of file diff --git a/web/src/components/GameMap.vue b/web/src/components/GameMap.vue new file mode 100644 index 0000000..6799c4f --- /dev/null +++ b/web/src/components/GameMap.vue @@ -0,0 +1,36 @@ + + + + + \ No newline at end of file diff --git a/web/src/components/NavBar.vue b/web/src/components/NavBar.vue new file mode 100644 index 0000000..e863d2c --- /dev/null +++ b/web/src/components/NavBar.vue @@ -0,0 +1,54 @@ + + + + + \ No newline at end of file diff --git a/web/src/components/PlayGround.vue b/web/src/components/PlayGround.vue new file mode 100644 index 0000000..c54c9df --- /dev/null +++ b/web/src/components/PlayGround.vue @@ -0,0 +1,24 @@ + + + + + \ No newline at end of file diff --git a/web/src/router/index.js b/web/src/router/index.js index d66c28f..9f4e607 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -1,6 +1,46 @@ import { createRouter, createWebHistory } from 'vue-router' +import PkIndexView from '../views/pk/PkIndexView' +import RecordIndexView from '../views/record/RecordIndexView' +import RanklistIndexView from '../views/ranklist/RanklistIndexView' +import UserBotIndexView from '../views/user/bot/UserBotIndexView' +import NotFound from '../views/error/NotFound' -const routes = [] +const routes = [ + { + path: "/", + name: "home", + redirect: "/pk/" + }, + { + path: "/pk/", + name: "pk_index", + component: PkIndexView, + }, + { + path: "/record/", + name: "record_index", + component: RecordIndexView, + }, + { + path: "/ranklist/", + name: "ranklist_index", + component: RanklistIndexView, + }, + { + path: "/user/bot/", + name: "user_bot_index", + component: UserBotIndexView, + }, + { + path: "/404/", + name: "404", + component: NotFound, + }, + { + path: "/:catchAll(.*)", + redirect: "/404/" + } +] const router = createRouter({ history: createWebHistory(), diff --git a/web/src/views/error/NotFound.vue b/web/src/views/error/NotFound.vue new file mode 100644 index 0000000..63c0f38 --- /dev/null +++ b/web/src/views/error/NotFound.vue @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/web/src/views/pk/PkIndexView.vue b/web/src/views/pk/PkIndexView.vue new file mode 100644 index 0000000..680e6a1 --- /dev/null +++ b/web/src/views/pk/PkIndexView.vue @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/web/src/views/ranklist/RanklistIndexView.vue b/web/src/views/ranklist/RanklistIndexView.vue new file mode 100644 index 0000000..5e0eafe --- /dev/null +++ b/web/src/views/ranklist/RanklistIndexView.vue @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/web/src/views/record/RecordIndexView.vue b/web/src/views/record/RecordIndexView.vue new file mode 100644 index 0000000..099a392 --- /dev/null +++ b/web/src/views/record/RecordIndexView.vue @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/web/src/views/user/bot/UserBotIndexView.vue b/web/src/views/user/bot/UserBotIndexView.vue new file mode 100644 index 0000000..62ead9c --- /dev/null +++ b/web/src/views/user/bot/UserBotIndexView.vue @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file