[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>围住小猫-By Mr yang</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Microsoft YaHei", "PingFang SC", -apple-system, sans-serif;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
display: flex;
flex-direction: column;
align-items: center;
overflow-x: hidden;
}
/* 页面容器 */
.page {
display: none;
width: 100%;
max-width: 100%;
padding: 20px;
animation: fadeIn 0.3s ease;
}
.page.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* ========== 选择页面 ========== */
.select-title {
font-size: 36px;
color: white;
text-shadow: 3px 3px 6px rgba(0,0,0,0.3);
margin: 30px 0 20px;
text-align: center;
font-weight: bold;
}
.select-subtitle {
color: rgba(255,255,255,0.9);
font-size: 16px;
text-align: center;
margin-bottom: 30px;
}
/* 猫咪卡片 */
.cat-cards {
display: flex;
flex-direction: column;
gap: 20px;
width: 100%;
max-width: 400px;
margin: 0 auto;
}
.cat-card {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 20px;
display: flex;
align-items: center;
gap: 20px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
border: 3px solid transparent;
}
.cat-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 40px rgba(0,0,0,0.25);
}
.cat-card:active {
transform: scale(0.98);
}
.cat-card-img {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
flex-shrink: 0;
}
.cat-card-info {
flex: 1;
}
.cat-card-name {
font-size: 22px;
font-weight: bold;
color: #333;
margin-bottom: 5px;
}
.cat-card-difficulty {
display: inline-block;
font-size: 13px;
padding: 4px 12px;
border-radius: 12px;
color: white;
margin-bottom: 8px;
}
.diff-low { background: linear-gradient(135deg, #52c41a, #73d13d); }
.diff-medium { background: linear-gradient(135deg, #faad14, #ffc53d); }
.diff-high { background: linear-gradient(135deg, #ff4d4f, #ff7875); }
.cat-card-desc {
font-size: 14px;
color: #666;
}
/* ========== 游戏页面 ========== */
.game-header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
max-width: 500px;
margin-bottom: 15px;
padding: 0 5px;
}
.back-btn {
background: rgba(255,255,255,0.2);
border: none;
color: white;
padding: 10px 15px;
border-radius: 20px;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
gap: 5px;
transition: 0.3s;
}
.back-btn:hover {
background: rgba(255,255,255,0.3);
}
.current-cat-badge {
background: rgba(255,255,255,0.9);
padding: 8px 16px;
border-radius: 20px;
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #333;
font-weight: bold;
}
.current-cat-badge img {
width: 24px;
height: 24px;
border-radius: 50%;
object-fit: cover;
}
.game-container {
background: rgba(255, 255, 255, 0.98);
border-radius: 20px;
padding: 20px;
box-shadow: 0 15px 60px rgba(0,0,0,0.2);
width: 100%;
max-width: 500px;
}
.game-status-bar {
display: flex;
justify-content: space-around;
margin-bottom: 15px;
padding: 10px;
background: #f8f9fa;
border-radius: 12px;
}
.status-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 3px;
}
.status-item .label {
font-size: 11px;
color: #999;
}
.status-item .value {
font-size: 15px;
font-weight: bold;
color: #333;
}
.status-text {
text-align: center;
font-size: 16px;
color: #667eea;
font-weight: bold;
margin-bottom: 10px;
min-height: 24px;
}
.status-text.win { color: #52c41a; }
.status-text.lose { color: #ff4d4f; }
.restart-hint {
text-align: center;
font-size: 13px;
color: #999;
margin-bottom: 10px;
opacity: 0;
transition: 0.3s;
}
.restart-hint.show { opacity: 1; }
/* Canvas容器 */
.canvas-wrapper {
display: flex;
justify-content: center;
padding: 10px;
background: linear-gradient(135deg, #667eea22, #764ba222);
border-radius: 15px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
canvas {
display: block;
cursor: pointer;
max-width: 100%;
height: auto;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
outline: none;
}
/* 难度说明 */
.difficulty-card {
margin-top: 15px;
padding: 15px;
background: #f8f9fa;
border-radius: 12px;
}
.difficulty-card h3 {
font-size: 14px;
color: #333;
margin-bottom: 10px;
text-align: center;
}
.difficulty-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.difficulty-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: #555;
}
.difficulty-item .emoji {
font-size: 16px;
}
/* 重新开始按钮 */
.restart-btn {
width: 100%;
margin-top: 15px;
padding: 14px;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
border-radius: 25px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
.restart-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}
.restart-btn:active {
transform: scale(0.98);
}
/* ========== 响应式适配 ========== */
[url=home.php?mod=space&uid=945662]@media[/url] (max-width: 480px) {
.select-title {
font-size: 28px;
margin: 20px 0 15px;
}
.select-subtitle {
font-size: 14px;
margin-bottom: 20px;
}
.cat-cards {
padding: 0 10px;
}
.cat-card {
padding: 15px;
gap: 15px;
}
.cat-card-img {
width: 65px;
height: 65px;
}
.cat-card-name {
font-size: 18px;
}
.cat-card-desc {
font-size: 13px;
}
.game-container {
padding: 15px;
border-radius: 15px;
}
.game-status-bar {
padding: 8px;
}
.status-item .label {
font-size: 10px;
}
.status-item .value {
font-size: 13px;
}
.difficulty-card {
padding: 12px;
}
.difficulty-item {
font-size: 11px;
}
}
/* 横屏适配 */
@media (max-height: 500px) and (orientation: landscape) {
.select-title {
font-size: 24px;
margin: 10px 0;
}
.select-subtitle {
display: none;
}
.cat-cards {
flex-direction: row;
max-width: 100%;
overflow-x: auto;
gap: 15px;
padding: 0 10px;
}
.cat-card {
flex-direction: column;
min-width: 140px;
padding: 15px;
text-align: center;
}
.cat-card-info {
display: flex;
flex-direction: column;
align-items: center;
}
}
</style>
</head>
<body>
<!-- ========== 选择猫咪页面 ========== -->
<div id="selectPage" class="page active">
<h1 class="select-title">围住小猫</h1>
<p class="select-subtitle">摸鱼摸鱼🐟️,打发时间....</p>
<div class="cat-cards">
<div class="cat-card" onclick="startGame('orange')">
<img class="cat-card-img" src="https://coze-coding-project.tos.coze.site/coze_storage_7633745811309953058/image/generate_image_fef41237-3294-4d26-961f-da549e688db7.jpeg?sign=1808906758-1f5b482dea-0-b5fb3a750ad553b048f383d5032514900fa9ff36037ca2705e1bedc1eb87270a" alt="橘猫">
<div class="cat-card-info">
<div class="cat-card-name">橘猫</div>
<span class="cat-card-difficulty diff-low">佛系</span>
<div class="cat-card-desc">走一步,慢悠悠的~</div>
</div>
</div>
<div class="cat-card" onclick="startGame('tabby')">
<img class="cat-card-img" src="https://coze-coding-project.tos.coze.site/coze_storage_7633745811309953058/image/generate_image_8fb2213f-8103-42db-ba18-5bacd5706749.jpeg?sign=1808906764-896eafba5d-0-8860c0eeb05efbbac4f103af0705d2a00434a32ba05358426e074f93627c6e7c" alt="虎斑猫">
<div class="cat-card-info">
<div class="cat-card-name">虎斑猫</div>
<span class="cat-card-difficulty diff-medium">敏捷</span>
<div class="cat-card-desc">走一步,速度更快~</div>
</div>
</div>
<div class="cat-card" onclick="startGame('lihua')">
<img class="cat-card-img" src="https://coze-coding-project.tos.coze.site/coze_storage_7633745811309953058/image/generate_image_2f4ca293-4b47-4254-9506-62759412fdcc.jpeg?sign=1808906761-46b3d7c170-0-ec10d13ced249bf5ff258ba5e164669c538f5a0c33d84f9fe18f0302e7e14b6a" alt="狸花猫">
<div class="cat-card-info">
<div class="cat-card-name">狸花猫</div>
<span class="cat-card-difficulty diff-high">狡猾</span>
<div class="cat-card-desc">走两步,心眼超多~</div>
</div>
</div>
</div>
</div>
<!-- ========== 游戏页面 ========== -->
<div id="gamePage" class="page">
<div class="game-header">
<button class="back-btn" onclick="goBack()">
<span><</span> 返回
</button>
<div class="current-cat-badge" id="currentCatBadge">
<img id="badgeCatImg" src="" alt="">
<span id="badgeCatName">橘猫</span>
</div>
</div>
<div class="game-container">
<div class="game-status-bar">
<div class="status-item">
<span class="label">放置次数</span>
<span class="value" id="moveCount">0</span>
</div>
<div class="status-item">
<span class="label">限制</span>
<span class="value">≤13步</span>
</div>
<div class="status-item">
<span class="label">难度</span>
<span class="value" id="difficultyText">佛系</span>
</div>
<div class="status-item">
<span class="label">猫咪位置</span>
<span class="value" id="catPosition">(5,5)</span>
</div>
</div>
<div id="status" class="status-text">点击蓝色圆点开始围堵</div>
<div id="restartHint" class="restart-hint">点击重新开始</div>
<div class="canvas-wrapper">
<canvas id="catCanvas"></canvas>
</div>
<div class="difficulty-card">
<h3>游戏说明</h3>
<div class="difficulty-list">
<div class="difficulty-item">
<span class="emoji">🐱</span>
<span>点击蓝色圆点放置障碍物</span>
</div>
<div class="difficulty-item">
<span class="emoji">🎯</span>
<span>围住猫咪 + ≤13步 = 胜利</span>
</div>
<div class="difficulty-item">
<span class="emoji" id="hintEmoji">🐱</span>
<span id="hintText">橘猫每次走一步</span>
</div>
<div class="difficulty-item" id="lihuaHint" style="display:none;">
<span class="emoji">⚠️</span>
<span>围堵太紧时狸花猫会愤怒跳两步!</span>
</div>
</div>
</div>
<button class="restart-btn" onclick="restartGame()">重新开始</button>
</div>
</div>
<script>
const canvas = document.getElementById('catCanvas');
const ctx = canvas.getContext('2d');
const statusText = document.getElementById('status');
const restartHint = document.getElementById('restartHint');
const moveCountDisplay = document.getElementById('moveCount');
const catPositionDisplay = document.getElementById('catPosition');
const difficultyText = document.getElementById('difficultyText');
const hintText = document.getElementById('hintText');
const hintEmoji = document.getElementById('hintEmoji');
const badgeCatImg = document.getElementById('badgeCatImg');
const badgeCatName = document.getElementById('badgeCatName');
// 游戏配置
const ROWS = 11, COLS = 11, RADIUS = 16, GAP = 6;
// 响应式调整画布大小
function resizeCanvas() {
const screenWidth = window.innerWidth;
let canvasWidth, canvasHeight;
if (screenWidth <= 480) {
// 手机端:缩小圆点和间距
canvas.width = COLS * (RADIUS * 2 + GAP) + RADIUS + 10;
canvas.height = ROWS * (RADIUS * 2 + 4) + 10;
} else {
// 桌面端
canvas.width = COLS * (RADIUS * 2 + GAP) + RADIUS + 15;
canvas.height = ROWS * (RADIUS * 2 + 4) + 15;
}
}
// 猫咪配置
const catConfigs = {
orange: {
name: '橘猫',
difficulty: '佛系',
steps: 1,
speed: 200,
furColor: '#FF8C00',
bellyColor: '#FFB347',
eyeColor: '#000',
emoji: '🐱'
},
tabby: {
name: '虎斑猫',
difficulty: '敏捷',
steps: 1,
speed: 120,
furColor: '#708090',
bellyColor: '#C0C0C0',
eyeColor: '#2F4F4F',
emoji: '🐯'
},
lihua: {
name: '狸花猫',
difficulty: '狡猾',
steps: 1, // 默认走一步
speed: 150,
furColor: '#8B4513',
bellyColor: '#DEB887',
eyeColor: '#006400',
emoji: '🦊',
angrySteps: 2 // 被激怒时走两步
}
};
// 猫咪图片
const catImages = {};
const catImageUrls = {
orange: 'https://coze-coding-project.tos.coze.site/coze_storage_7633745811309953058/image/generate_image_fef41237-3294-4d26-961f-da549e688db7.jpeg?sign=1808906758-1f5b482dea-0-b5fb3a750ad553b048f383d5032514900fa9ff36037ca2705e1bedc1eb87270a',
tabby: 'https://coze-coding-project.tos.coze.site/coze_storage_7633745811309953058/image/generate_image_8fb2213f-8103-42db-ba18-5bacd5706749.jpeg?sign=1808906764-896eafba5d-0-8860c0eeb05efbbac4f103af0705d2a00434a32ba05358426e074f93627c6e7c',
lihua: 'https://coze-coding-project.tos.coze.site/coze_storage_7633745811309953058/image/generate_image_2f4ca293-4b47-4254-9506-62759412fdcc.jpeg?sign=1808906761-46b3d7c170-0-ec10d13ced249bf5ff258ba5e164669c538f5a0c33d84f9fe18f0302e7e14b6a'
};
// 游戏状态
let currentCatType = 'orange';
let moveCount = 0;
let map = [], catPos = { r: 5, c: 5 }, catDrawPos = { x: 0, y: 0 }, catDir = 1;
let isOver = false, isAnimating = false;
let aniId = null;
// 预加载猫咪图片
function preloadCatImages() {
return new Promise((resolve) => {
let loaded = 0;
const total = Object.keys(catImageUrls).length;
Object.keys(catImageUrls).forEach(type => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
catImages[type] = img;
loaded++;
if (loaded >= total) resolve();
};
img.onerror = () => {
loaded++;
if (loaded >= total) resolve();
};
img.src = catImageUrls[type];
});
});
}
// 页面切换
function showPage(pageId) {
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
document.getElementById(pageId).classList.add('active');
}
// 开始游戏
function startGame(type) {
currentCatType = type;
const config = catConfigs[type];
// 更新顶部徽章
badgeCatImg.src = catImageUrls[type];
badgeCatName.textContent = config.name;
difficultyText.textContent = config.difficulty;
hintEmoji.textContent = config.emoji;
hintText.textContent = `${config.name}每次走${config.steps}步`;
// 狸花猫特殊提示
const lihuaHint = document.getElementById('lihuaHint');
if (type === 'lihua') {
lihuaHint.style.display = 'flex';
} else {
lihuaHint.style.display = 'none';
}
// 初始化游戏
resizeCanvas();
initMap();
// 切换到游戏页面
showPage('gamePage');
}
// 返回选择页面
function goBack() {
if (aniId) cancelAnimationFrame(aniId);
showPage('selectPage');
}
// 重新开始游戏
function restartGame() {
initMap();
}
function initMap() {
if (aniId) cancelAnimationFrame(aniId);
isOver = false; isAnimating = false;
moveCount = 0;
moveCountDisplay.textContent = '0';
const config = catConfigs[currentCatType];
statusText.innerText = `选择${config.name},点击蓝点围住它!`;
statusText.className = "status-text";
restartHint.className = "restart-hint";
map = Array.from({ length: ROWS }, () => Array(COLS).fill(0));
let minObs = currentCatType === 'lihua' ? 10 : currentCatType === 'tabby' ? 11 : 12;
let maxObs = currentCatType === 'lihua' ? 14 : currentCatType === 'tabby' ? 15 : 18;
let count = minObs + Math.floor(Math.random() * (maxObs - minObs + 1));
while (count > 0) {
let r = Math.floor(Math.random() * ROWS), c = Math.floor(Math.random() * COLS);
if (map[r][c] === 0 && !(r === 5 && c === 5)) {
map[r][c] = 1;
count--;
}
}
catPos = { r: 5, c: 5 };
catDrawPos = getXY(catPos.r, catPos.c);
catPositionDisplay.textContent = `(5,5)`;
startLoop();
}
function getXY(r, c) {
const x = RADIUS + 6 + c * (RADIUS * 2 + GAP) + (r % 2 === 1 ? RADIUS : 0);
const y = RADIUS + 6 + r * (RADIUS * 2 + 4);
return { x, y };
}
// 绘制猫咪
function drawCat(x, y, dir, isRunning) {
if (isNaN(x) || isNaN(y)) return;
ctx.save();
const config = catConfigs[currentCatType];
const t = Date.now();
let bobY = isRunning ? Math.sin(t / 35) * 3 : Math.sin(t / 400) * 0.8;
ctx.translate(x, y + bobY);
ctx.scale(dir, 1);
const furColor = config.furColor;
const bellyColor = config.bellyColor;
// 身体
ctx.fillStyle = furColor;
ctx.beginPath();
ctx.roundRect(-12, -6, 24, 14, 6);
ctx.fill();
// 肚子
ctx.fillStyle = bellyColor;
ctx.beginPath();
ctx.ellipse(0, 2, 8, 4, 0, 0, Math.PI*2);
ctx.fill();
// 条纹
if (currentCatType === 'tabby' || currentCatType === 'lihua') {
ctx.strokeStyle = currentCatType === 'tabby' ? '#2F2F2F' : '#3D2314';
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.moveTo(-6, -5); ctx.lineTo(-6, 4);
ctx.moveTo(0, -5); ctx.lineTo(0, 4);
ctx.moveTo(6, -5); ctx.lineTo(6, 4);
ctx.stroke();
}
// 头
ctx.fillStyle = furColor;
ctx.beginPath();
ctx.arc(10, -5, 9, 0, Math.PI * 2);
ctx.fill();
// 耳朵
ctx.beginPath(); ctx.moveTo(7, -12); ctx.lineTo(13, -12); ctx.lineTo(10, -20); ctx.fill();
ctx.beginPath(); ctx.moveTo(12, -10); ctx.lineTo(18, -8); ctx.lineTo(16, -16); ctx.fill();
// 耳内
ctx.fillStyle = '#FFB6C1';
ctx.beginPath(); ctx.moveTo(8, -12); ctx.lineTo(11, -12); ctx.lineTo(9.5, -17); ctx.fill();
ctx.beginPath(); ctx.moveTo(13, -10); ctx.lineTo(16, -9); ctx.lineTo(15, -14); ctx.fill();
// 眼睛
ctx.fillStyle = "#FFF";
ctx.beginPath(); ctx.arc(13, -6, 2, 0, Math.PI*2); ctx.fill();
ctx.fillStyle = config.eyeColor;
ctx.beginPath(); ctx.arc(14, -6, 1, 0, Math.PI*2); ctx.fill();
// 鼻子
ctx.fillStyle = '#FFB6C1';
ctx.beginPath();
ctx.moveTo(17, -3);
ctx.lineTo(15, -1);
ctx.lineTo(19, -1);
ctx.closePath();
ctx.fill();
// 腮红
ctx.fillStyle = 'rgba(255, 182, 193, 0.4)';
ctx.beginPath(); ctx.ellipse(19, -1, 2.5, 1.5, 0, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.ellipse(7, -1, 2.5, 1.5, 0, 0, Math.PI*2); ctx.fill();
// 腿
ctx.fillStyle = furColor;
ctx.beginPath(); ctx.roundRect(7, 5, 4, 8, 2); ctx.fill();
ctx.beginPath(); ctx.roundRect(-10, 5, 4, 8, 2); ctx.fill();
// 尾巴
ctx.lineWidth = 3; ctx.strokeStyle = furColor; ctx.lineCap = "round";
ctx.beginPath(); ctx.moveTo(-12, -2); ctx.quadraticCurveTo(-20, -6, -18, -14); ctx.stroke();
ctx.restore();
}
function startLoop() {
const loop = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let r = 0; r < ROWS; r++) {
for (let c = 0; c < COLS; c++) {
const { x, y } = getXY(r, c);
ctx.beginPath(); ctx.arc(x, y, RADIUS, 0, Math.PI * 2);
ctx.fillStyle = map[r][c] === 1 ? "#34495e" : "#b3d9ff"; ctx.fill();
}
}
const catBase = getXY(catPos.r, catPos.c);
if (!isAnimating || (isAnimating && !isOver)) {
ctx.beginPath(); ctx.arc(catBase.x, catBase.y, RADIUS, 0, Math.PI*2);
ctx.fillStyle = "#b3d9ff"; ctx.fill();
}
drawCat(catDrawPos.x, catDrawPos.y, catDir, isAnimating && !isOver);
aniId = requestAnimationFrame(loop);
};
loop();
}
function getAdj(r, c) {
let n = [[r, c-1], [r, c+1]];
if (r % 2 === 0) n.push([r-1, c-1], [r-1, c], [r+1, c-1], [r+1, c]);
else n.push([r-1, c], [r-1, c+1], [r+1, c], [r+1, c+1]);
return n.filter(p => p[0]>=0 && p[0]<ROWS && p[1]>=0 && p[1]<COLS);
}
function findSimplePath(startR, startC) {
let queue = [{ r: startR, c: startC, path: [] }], visited = new Set([`${startR},${startC}`]);
while (queue.length > 0) {
let curr = queue.shift();
if (curr.r === 0 || curr.r === ROWS - 1 || curr.c === 0 || curr.c === COLS - 1) return curr.path;
for (let [nr, nc] of getAdj(curr.r, curr.c)) {
if (map[nr][nc] === 0 && !visited.has(`${nr},${nc}`)) {
visited.add(`${nr},${nc}`);
queue.push({ r: nr, c: nc, path: curr.path.concat({ r: nr, c: nc }) });
}
}
}
return null;
}
function catMove() {
if (isOver || isAnimating) return;
const config = catConfigs[currentCatType];
let path = findSimplePath(catPos.r, catPos.c);
if (!path || path.length === 0) {
// 猫被困住了,检查放置次数
if (moveCount <= 13) {
isOver = true;
const winMessages = [
`${config.name}已经无路可走,你赢了!`,
`太棒了!${config.name}被困住了!`,
`成功围住${config.name}!`
];
statusText.innerText = winMessages[Math.floor(Math.random() * winMessages.length)];
statusText.className = "status-text win";
} else {
isOver = true;
statusText.innerText = `超过13步限制,${config.name}成功坚守!`;
statusText.className = "status-text lose";
}
restartHint.className = "restart-hint show";
return;
}
isAnimating = true;
// 狸花猫特殊机制:当周围逃跑路线 ≤3 时被激怒跳两步
let stepsToMove = 1;
if (currentCatType === 'lihua' && config.angrySteps) {
const escapeRoutes = countEscapeRoutes(catPos.r, catPos.c);
if (escapeRoutes <= 3) {
stepsToMove = 2;
statusText.innerText = `狸花猫被激怒了!跳两步逃跑!`;
}
}
// 执行移动
const actualSteps = Math.min(stepsToMove, path.length);
animateCatMove(path.slice(0, actualSteps), () => {
isAnimating = false;
});
}
// 计算某位置的可逃跑方向数量
function countEscapeRoutes(r, c) {
let count = 0;
const adj = getAdj(r, c);
for (let [nr, nc] of adj) {
if (map[nr][nc] === 0) {
count++;
}
}
return count;
}
function animateCatMove(pathSegments, onComplete) {
if (pathSegments.length === 0) {
onComplete();
return;
}
const config = catConfigs[currentCatType];
const segment = pathSegments[0];
const startXY = { ...catDrawPos };
const endXY = getXY(segment.r, segment.c);
catDir = endXY.x < startXY.x ? -1 : 1;
catPos = { r: segment.r, c: segment.c };
catPositionDisplay.textContent = `(${segment.r},${segment.c})`;
let startTime = null;
const duration = config.speed;
const aniStep = (time) => {
if (!startTime) startTime = time;
const p = Math.min((time - startTime) / duration, 1);
catDrawPos.x = startXY.x + (endXY.x - startXY.x) * p;
catDrawPos.y = startXY.y + (endXY.y - startXY.y) * p;
if (p < 1) {
requestAnimationFrame(aniStep);
} else {
if (pathSegments.length > 1) {
animateCatMove(pathSegments.slice(1), onComplete);
} else {
if (catPos.r === 0 || catPos.r === ROWS-1 || catPos.c === 0 || catPos.c === COLS-1) {
// 猫逃跑了,检查放置次数
if (moveCount <= 13) {
isOver = true;
const loseMessages = [
`哎呀,${config.name}逃跑了!`,
`${config.name}跑掉了,下次再战!`,
`被${config.name}溜走了~`
];
statusText.innerText = loseMessages[Math.floor(Math.random() * loseMessages.length)];
statusText.className = "status-text lose";
} else {
isOver = true;
statusText.innerText = `超过13步限制,${config.name}成功逃脱!`;
statusText.className = "status-text lose";
}
restartHint.className = "restart-hint show";
} else {
onComplete();
}
}
}
};
requestAnimationFrame(aniStep);
}
canvas.addEventListener('click', (e) => {
if (isOver) { initMap(); return; }
if (isAnimating) return;
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const mX = (e.clientX - rect.left) * scaleX;
const mY = (e.clientY - rect.top) * scaleY;
for (let r = 0; r < ROWS; r++) {
for (let c = 0; c < COLS; c++) {
const { x, y } = getXY(r, c);
if (Math.hypot(mX - x, mY - y) < RADIUS + 8) {
if (map[r][c] === 0 && !(r === catPos.r && c === catPos.c)) {
const config = catConfigs[currentCatType];
moveCount++;
moveCountDisplay.textContent = moveCount;
// 检查是否超过13步限制
if (moveCount > 13) {
isOver = true;
statusText.innerText = `超过13步限制,${config.name}成功坚守!`;
statusText.className = "status-text lose";
restartHint.className = "restart-hint show";
return;
}
statusText.innerText = `放置障碍物,${config.name}要动了!`;
map[r][c] = 1;
catMove();
return;
}
}
}
}
});
// 监听窗口大小变化
window.addEventListener('resize', () => {
if (document.getElementById('gamePage').classList.contains('active')) {
resizeCanvas();
}
});
// 初始化
preloadCatImages();
</script>
</body>
</html>