网站背景图
个人成长计划 利她,有价值,不打扰。
博主

一小时前在线

个人成长计划
利她,有价值,不打扰。

沪ICP备2022018381号-2

京公网安备11011502037542

网站已运行 5 年 325 天 19 小时 18 分

Powered by Typecho & Sunny

23 online · 21 ms

文章

为博客添加404页面

ayiya

·

折腾笔记

·

前阵子不是给博客搬家了么,导致好多链接无法访问,我就想着弄一个404页面但不想太单调了,就把52破解的404搬过来改了下玩法提高了难度还挺好玩😂
QQ20260808-001243.png

优化后玩法

点击格子放围墙,把小丑围住即胜利;小丑跑到棋盘边缘则失败。 紫色为陷阱格子,小丑踩到会被禁行动一回合;金色为幸运格子,放置围墙后本回合小丑直接不能移动。
每8个玩家回合触发一次微风或迷雾
🌬微风:随机拆掉场上一堵普通围墙(不会拆陷阱、幸运格子)。
🌀迷雾:下一回合小丑不会走最优逃跑路线,会在可行格子里随机乱走。

代码是用豆包帮我完善的还挺不错。

完整代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>围堵小丑</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        html, body {
            height: 100%;
        }
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            background-color: #f8f9fa;
            color: #333;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            text-align: center;
            padding: 20px;
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale;
        }
        .container {
            max-width: 480px;
            width: 100%;
            animation: fadeIn 0.5s ease-out;
        }
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(10px); }
            to   { opacity: 1; transform: translateY(0); }
        }
        h1.error-code {
            font-size: 5.5rem;
            color: #e53935;
            margin-bottom: 10px;
            font-weight: 700;
            line-height: 1;
        }
        h2.sub-title {
            font-size: 1.25rem;
            color: #444;
            margin-bottom: 12px;
            font-weight: 500;
        }
        .desc {
            font-size: 0.95rem;
            color: #666;
            line-height: 1.6;
            margin-bottom: 8px;
        }
        .score-info{
            font-size:0.85rem;
            color:#666;
            margin-bottom:12px;
        }
        #status {
            font-size: 0.9rem;
            color: #444;
            margin-bottom: 14px;
            min-height: 20px;
            font-weight: 500;
            transition: color 0.25s ease;
        }
        #status.win  { color: #28a745; }
        #status.lose { color: #e53935; }
        #status.warn { color: #ff7700; }
        #status.trap { color: #9c27b0; }
        #status.event { color:#d48806; }
        .game-wrap {
            margin-bottom: 18px;
            display: inline-block;
            border: 1px solid #dee2e6;
            border-radius: 10px;
            padding: 12px;
            background: #ffffff;
        }
        canvas {
            display: block;
            cursor: pointer;
            margin: 0 auto;
            -webkit-tap-highlight-color: transparent;
            border-radius: 6px;
            max-height: 52vh;
            width: auto;
        }
        .reset-row {
            margin-bottom: 22px;
        }
        .btn-reset {
            font-size: 0.85rem;
            color: #0d6efd;
            background: none;
            border: none;
            cursor: pointer;
            padding: 4px 10px;
            border-radius: 6px;
            transition: background 0.2s ease;
        }
        .btn-reset:hover {
            background: #e7f1ff;
        }
        .btn-group {
            display: flex;
            gap: 14px;
            justify-content: center;
        }
        .btn {
            flex: 1;
            max-width: 160px;
            padding: 10px 20px;
            border-radius: 8px;
            text-decoration: none;
            font-size: 0.9rem;
            font-weight: 500;
            transition: all 0.2s ease;
            border: none;
            cursor: pointer;
        }
        .btn-primary {
            background: #0d6efd;
            color: #fff;
        }
        .btn-primary:hover {
            background: #0b5ed7;
            transform: translateY(-1px);
            box-shadow: 0 4px 12px rgba(13, 110, 253, 0.25);
        }
        .btn-secondary {
            background: #ffffff;
            color: #495057;
            border: 1px solid #dee2e6;
        }
        .btn-secondary:hover {
            background: #f8f9fa;
            transform: translateY(-1px);
        }
    </style>
</head>
<body>
    <div class="container">
        <h1 class="error-code">404</h1>
        <h2 class="sub-title">Not Found · 页面未找到</h2>
        <p class="desc">
            您所访问的页面不存在或者已更新,请跳转博客或者返回书签
        </p>
        <div class="score-info" id="scoreInfo"></div>
        <div id="status">点击小圆点,围住小丑</div>
        <div class="game-wrap">
            <canvas id="game"></canvas>
        </div>
        <div class="reset-row">
            <button class="btn-reset" id="resetBtn">重新开始</button>
        </div>
        <div class="btn-group">
            <a href="https://blog.el9.cn" class="btn btn-primary">返回博客</a>
            <a href="https://el9.cn" class="btn btn-secondary">返回书签</a>
        </div>
    </div>

<script>
(function() {
    const canvas = document.getElementById('game');
    const ctx = canvas.getContext('2d');
    const statusEl = document.getElementById('status');
    const resetBtn = document.getElementById('resetBtn');
    const scoreInfoEl = document.getElementById('scoreInfo');

    const ROW = 11;
    const COL = 11;
    const R = 14;
    const GAP = 5;
    const STEP_TIME = 150;
    const DOUBLE_MOVE_CHANCE = 0.3;
    const EVENT_INTERVAL = 8;
    const dpr = window.devicePixelRatio || 1;

    //小丑出生点周围6个格子
    const CLOWN_START_NEIGHBORS = [[4,5],[5,4],[5,6],[6,5],[4,4],[6,6]];

    let map = [];
    let trapMap = [];
    let luckyCell = null;
    let clown = { r:5, c:5 };
    let clownDraw = { x:0, y:0 };

    let gameOver = false;
    let moving = false;
    let loopId = null;
    let moveAniId = null;

    let nextStepCount = 1;
    let justDoubleMove = false;
    let turnCount = 0;
    let eventCounter = 0;
    let winCount = 0;
    let failCount = 0;
    let skipTurn = false;
    let eventConfuse = false;

    //本地存储
    function loadStats(){
        winCount = Number(localStorage.getItem("clownWinCount"))||0;
        failCount = Number(localStorage.getItem("clownFailCount"))||0;
        renderScoreText();
    }
    function saveWin(){
        winCount +=1;
        localStorage.setItem("clownWinCount", String(winCount));
        renderScoreText();
    }
    function addFail(){
        failCount +=1;
        localStorage.setItem("clownFailCount", String(failCount));
        renderScoreText();
    }
    function renderScoreText(){
        scoreInfoEl.textContent = `本局回合:${turnCount}|累计胜利:${winCount}局|累计失败:${failCount}局`;
    }

    //获取画布坐标
    function getPos(r, c) {
        const x = R + 5 + c * (R * 2 + GAP) + (r % 2 === 1 ? R + GAP / 2 : 0);
        const y = R + 5 + r * (R * 2 + GAP);
        return { x, y };
    }

    //获取六边形相邻格子
    function getNeighbors(r, c) {
        const neighbors = [[r, c-1],[r, c+1]];
        if(r%2===0){
            neighbors.push([r-1,c-1],[r-1,c],[r+1,c-1],[r+1,c]);
        }else{
            neighbors.push([r-1,c],[r-1,c+1],[r+1,c],[r+1,c+1]);
        }
        return neighbors.filter(([nr,nc])=> nr>=0&&nr<ROW&&nc>=0&&nc<COL);
    }

    function isNearActiveTrap(r,c){
        return getNeighbors(r,c).some(([nr,nc])=> trapMap[nr][nc]===1);
    }

    //BFS逃跑距离
    function calcDistMap() {
        const dist = Array.from({length:ROW}, ()=>Array(COL).fill(Infinity));
        const queue = [];
        for(let r=0;r<ROW;r++){
            for(let c=0;c<COL;c++){
                if((r===0||r===ROW-1||c===0||c===COL-1) && map[r][c]===0){
                    dist[r][c]=1;
                    queue.push([r,c]);
                }
            }
        }
        while(queue.length>0){
            const [cr,cc] = queue.shift();
            for(const [nr,nc] of getNeighbors(cr,cc)){
                if(map[nr][nc]===0 && dist[nr][nc]===Infinity){
                    dist[nr][nc] = dist[cr][cc]+1;
                    queue.push([nr,nc]);
                }
            }
        }
        return dist;
    }

    //随机事件(移除警觉,仅微风、迷雾)
    function triggerRandomEvent(){
        const events = ["wind","confuse"];
        const e = events[Math.floor(Math.random()*events.length)];
        switch(e){
            case "wind":{
                const wallList = [];
                for(let r=0;r<ROW;r++){
                    for(let c=0;c<COL;c++){
                        if(map[r][c]===1 && !(r===5&&c===5)) wallList.push({r,c});
                    }
                }
                if(wallList.length>2){
                    const t = wallList[Math.floor(Math.random()*wallList.length)];
                    map[t.r][t.c]=0;
                }
                statusEl.textContent = "🌬事件:微风袭来,一面围墙被吹倒!";
                statusEl.className = "event";
                break;
            }
            case "confuse":{
                eventConfuse = true;
                statusEl.textContent = "🌀事件:迷雾迷惑,小丑下一回合会乱走!";
                statusEl.className = "event";
                break;
            }
        }
    }

    function drawClown(x,y){
        ctx.lineWidth = 1.6;
        ctx.strokeStyle = '#d17a00';
        ctx.fillStyle = 'rgba(209,122,0,0.08)';
        ctx.beginPath(); ctx.arc(x,y+2,R-3,0,Math.PI*2); ctx.fill();

        ctx.fillStyle = '#ff9f43';
        ctx.beginPath(); ctx.arc(x,y,R-3.5,0,Math.PI*2); ctx.fill(); ctx.stroke();

        ctx.fillStyle = '#ff5252';
        ctx.beginPath();
        ctx.moveTo(x-5,y-7); ctx.lineTo(x,y-15); ctx.lineTo(x+5,y-7); ctx.closePath();
        ctx.fill(); ctx.stroke();

        ctx.fillStyle = '#ffeb3b';
        ctx.beginPath(); ctx.arc(x,y-16,1.8,0,Math.PI*2); ctx.fill();

        ctx.fillStyle='#fff';
        ctx.beginPath(); ctx.arc(x-3,y-2,2.4,0,Math.PI*2); ctx.arc(x+3,y-2,2.4,0,Math.PI*2); ctx.fill();

        ctx.fillStyle='#111';
        ctx.beginPath(); ctx.arc(x-3,y-2,1.3,0,Math.PI*2); ctx.arc(x+3,y-2,1.3,0,Math.PI*2); ctx.fill();

        ctx.fillStyle='#fff';
        ctx.beginPath(); ctx.arc(x-3.5,y-2.8,0.7,0,Math.PI*2); ctx.arc(x+2.5,y-2.8,0.7,0,Math.PI*2); ctx.fill();

        ctx.fillStyle='#e53935';
        ctx.beginPath(); ctx.arc(x,y+2.5,1.8,0,Math.PI*2); ctx.fill();

        ctx.strokeStyle='#d17a00'; ctx.lineWidth=1.2;
        ctx.beginPath(); ctx.arc(x,y+3.5,4,0.15*Math.PI,0.85*Math.PI); ctx.stroke();
    }

    function render(){
        ctx.clearRect(0,0,canvas.width,canvas.height);
        for(let r=0;r<ROW;r++){
            for(let c=0;c<COL;c++){
                const {x,y}=getPos(r,c);
                ctx.beginPath(); ctx.arc(x,y,R,0,Math.PI*2);
                if(luckyCell && luckyCell.r===r && luckyCell.c===c){
                    ctx.fillStyle="#ffd700";
                }else if(map[r][c]===1){
                    ctx.fillStyle="#34495e";
                }else if(trapMap[r][c]===1){
                    ctx.fillStyle="#ce93d8";
                }else{
                    ctx.fillStyle="#b3d9ff";
                }
                ctx.fill();
            }
        }
        drawClown(clownDraw.x, clownDraw.y);
        loopId = requestAnimationFrame(render);
    }

    function rollNextStep(){
        if(skipTurn){
            skipTurn = false;
            nextStepCount = 0;
            statusEl.textContent = '⚠ 小丑踩到陷阱,本回合无法移动!';
            statusEl.className = 'trap';
            return;
        }
        if(justDoubleMove){
            nextStepCount = 1;
            justDoubleMove = false;
        }else{
            nextStepCount = isNearActiveTrap(clown.r,clown.c) ? 1 : (Math.random() < DOUBLE_MOVE_CHANCE ? 2 : 1);
        }
        if(nextStepCount === 2){
            statusEl.textContent = '⚠ 下回合小丑将走 2 步!';
            statusEl.className = 'warn';
        }else{
            statusEl.textContent = '点击小圆点,围住小丑';
            statusEl.className = '';
        }
    }

    function clownMove(){
        if(gameOver || moving) return;
        if(nextStepCount === 0){
            moving = true;
            setTimeout(()=>{ moving=false; rollNextStep(); },300);
            return;
        }

        let stepsCompleted = 0;
        moving = true;
        if(nextStepCount === 2) justDoubleMove = true;

        function doOneStep(){
            const distMap = calcDistMap();
            if(distMap[clown.r][clown.c] === Infinity){
                gameOver = true;
                statusEl.textContent = '你赢了!小丑被围住了';
                statusEl.className = 'win';
                moving = false;
                saveWin();
                return;
            }

            const neighbors = getNeighbors(clown.r,clown.c).filter(([nr,nc])=>map[nr][nc]===0);
            if(neighbors.length === 0){
                gameOver = true;
                statusEl.textContent = '你赢了!小丑被围住了';
                statusEl.className = 'win';
                moving = false;
                saveWin();
                return;
            }

            let pick;
            if(eventConfuse){
                const idx = Math.floor(Math.random()*neighbors.length);
                pick = {r:neighbors[idx][0], c:neighbors[idx][1]};
                eventConfuse = false;
            }else{
                const candidates = [];
                for(const [nr,nc] of neighbors){
                    const d = distMap[nr][nc];
                    if(d===Infinity) continue;
                    candidates.push({r:nr,c:nc, dist:d, isTrap: trapMap[nr][nc]===1});
                }
                candidates.sort((a,b)=>{
                    if(a.dist !== b.dist) return a.dist - b.dist;
                    if(a.isTrap !== b.isTrap) return a.isTrap ? 1 : -1;
                    return 0;
                });
                const bestDist = candidates[0].dist;
                const filtered = candidates.filter(it=>it.dist===bestDist);
                pick = filtered[Math.floor(Math.random()*filtered.length)];
            }

            const next = {r:pick.r, c:pick.c};
            const start = {...clownDraw};
            const end = getPos(next.r, next.c);
            const startTime = performance.now();

            function animateStep(now){
                const p = Math.min((now-startTime)/STEP_TIME,1);
                clownDraw.x = start.x + (end.x-start.x)*p;
                clownDraw.y = start.y + (end.y-start.y)*p;
                if(p<1){
                    moveAniId = requestAnimationFrame(animateStep);
                }else{
                    clown = next;
                    if(trapMap[clown.r][clown.c]===1){
                        trapMap[clown.r][clown.c]=2;
                        skipTurn = true;
                    }
                    stepsCompleted++;
                    if(clown.r===0||clown.r===ROW-1||clown.c===0||clown.c===COL-1){
                        gameOver = true;
                        statusEl.textContent = '你输了!小丑跑掉了';
                        statusEl.className = 'lose';
                        moving = false;
                        addFail();
                        return;
                    }
                    if(stepsCompleted < nextStepCount){
                        doOneStep();
                    }else{
                        moving = false;
                        rollNextStep();
                    }
                }
            }
            moveAniId = requestAnimationFrame(animateStep);
        }
        doOneStep();
    }

    function initCanvasSize(){
        const maxX = Math.max(getPos(0,COL-1).x, getPos(1,COL-1).x)+R+5;
        const cssHeight = getPos(ROW-1,0).y + R +5;
        canvas.width = maxX * dpr;
        canvas.height = cssHeight * dpr;
        canvas.style.width = maxX + 'px';
        canvas.style.height = cssHeight + 'px';
        ctx.scale(dpr, dpr);
    }

    function initGame(){
        if(loopId) cancelAnimationFrame(loopId);
        if(moveAniId) cancelAnimationFrame(moveAniId);

        gameOver = false;
        moving = false;
        turnCount = 0;
        eventCounter = 0;
        justDoubleMove = false;
        skipTurn = false;
        eventConfuse = false;
        luckyCell = null;

        map = Array.from({length:ROW}, ()=>Array(COL).fill(0));
        trapMap = Array.from({length:ROW}, ()=>Array(COL).fill(0));

        //【强制恰好4堵出生围墙】对6个周边格子洗牌,取前4个设墙
        const shuffled = [...CLOWN_START_NEIGHBORS].sort(()=>Math.random()-0.5);
        for(let i=0;i<4;i++){
            const [r,c] = shuffled[i];
            map[r][c]=1;
        }

        //生成其余随机障碍
        let obstacleNum = 8 + Math.floor(Math.random()*4);
        while(obstacleNum>0){
            const r = Math.floor(Math.random()*ROW);
            const c = Math.floor(Math.random()*COL);
            const dist = Math.abs(r-5)+Math.abs(c-5);
            if(map[r][c]===0 && !(r===5&&c===5) && dist>1){
                map[r][c]=1;
                obstacleNum--;
            }
        }

        //生成幸运格
        while(true){
            const lr = Math.floor(Math.random()*ROW);
            const lc = Math.floor(Math.random()*COL);
            const dist = Math.abs(lr-5)+Math.abs(lc-5);
            if(map[lr][lc]===0 && dist>=2){
                luckyCell = {r:lr,c:lc};
                break;
            }
        }

        //生成陷阱
        const trapCount = 2 + Math.floor(Math.random()*2);
        let tc = 0;
        while(tc < trapCount){
            const tr = Math.floor(Math.random()*ROW);
            const tc_ = Math.floor(Math.random()*COL);
            const dist = Math.abs(tr-5)+Math.abs(tc_-5);
            if(map[tr][tc_]===0 && trapMap[tr][tc_]===0 && dist>=2 && !(luckyCell && luckyCell.r===tr && luckyCell.c===tc_)){
                trapMap[tr][tc_]=1;
                tc++;
            }
        }

        //校验地图,小丑无路可逃就重生成
        const distMap = calcDistMap();
        if(distMap[5][5] === Infinity){
            return initGame();
        }

        clown = {r:5,c:5};
        clownDraw = getPos(clown.r, clown.c);
        rollNextStep();
        renderScoreText();
        render();
    }

    canvas.addEventListener('click', (e)=>{
        if(gameOver){
            initGame();
            return;
        }
        if(moving) return;

        const rect = canvas.getBoundingClientRect();
        const mx = e.clientX - rect.left;
        const my = e.clientY - rect.top;
        const scaleX = canvas.width / dpr / rect.width;
        const scaleY = canvas.height / dpr / rect.height;
        const realX = mx * scaleX;
        const realY = my * scaleY;

        for(let r=0;r<ROW;r++){
            for(let c=0;c<COL;c++){
                const {x,y}=getPos(r,c);
                const dist = Math.hypot(realX-x, realY-y);
                if(dist < R){
                    if(map[r][c]===0 && !(r===clown.r && c===clown.c)){
                        let luckyTriggered = false;
                        if(luckyCell && luckyCell.r===r && luckyCell.c===c){
                            luckyTriggered = true;
                            luckyCell = null;
                        }
                        map[r][c]=1;
                        turnCount++;
                        eventCounter++;
                        renderScoreText();

                        if(luckyTriggered){
                            statusEl.textContent="✨幸运格生效!小丑本次无法移动!";
                            statusEl.className="trap";
                            return;
                        }
                        if(eventCounter >= EVENT_INTERVAL){
                            eventCounter = 0;
                            triggerRandomEvent();
                            return;
                        }
                        clownMove();
                    }
                    return;
                }
            }
        }
    });

    resetBtn.addEventListener('click', initGame);

    loadStats();
    initCanvasSize();
    initGame();
})();
</script>
</body>
</html>
现在已有 18 次阅读,0 条评论,1 人点赞
作者:ayiya
作者
为博客添加404页面
当前文章累计共 20640 字,阅读大概需要 2 分钟。
卡片收藏整理
2021年12月21日 - 0评论
Vmware未能启动虚拟机
2026年6月25日 - 1评论
PureRef 汉化版
2025年3月20日 - 0评论
评论:共0条
发表
搜索 消息 足迹 排行
你还不曾留言过..
你还不曾留下足迹..
博主 📢 评论数据丢失说明
因老卢水平一般能力有限,在切换博客系统时
导致 2026年07月09日 以前 的评论数据丢失,
在此表示抱歉哈😂以后再也不瞎折腾了
不再显示
博主
未知作品 歌曲封面
博主 立即安装