HTML5实现一笔画游戏

news2024/12/25 13:54:55

HTML5实现一笔画游戏

一笔画问题

一笔画是图论科普中一个著名的问题,它起源于柯尼斯堡七桥问题科普。当时的东普鲁士哥尼斯堡城中有一条河,在这条河上有七座桥:

蓝色的代表河,这条河将城市分开成为四个区域,而七个橙色的矩形为座桥。

欧拉把实际的问题抽象为平面上的点与线,每一座桥视为一条线,桥所连接的地区视为点。

 “一笔画”问题涉及的核心概念包括连通图、奇点、偶点等。连通图指的是图中任意两个顶点之间都存在一条路径相连且没有重复。奇点则是与奇数个边相连的顶点,偶点则是与偶数个边相连的顶点。

欧拉发现,一个连通图能够一笔画出的条件是:要么图中所有顶点都是偶点,要么图中只有两个奇点。这个规律被称为欧拉定理,它为解决一笔画问题提供了理论基础。

由于哥尼斯堡七桥问题的抽象图中的四个顶点全部是奇顶点,所以它无法实现符合要求的走法,也就是不可能一笔画成。

数学家欧拉在他1736年发表的论文《柯尼斯堡的七桥》中不仅解决了七桥问题,也提出了一笔画定理,顺带解决了一笔画问题。他的这篇论文也成为图论史上第一篇重要文献。

HTML5实现一笔画游戏

HTML5和Canvas API提供了强大的图形处理能力,足以支持一笔画这样的游戏开发。

先给出效果图示:

游戏由两个文件构成:html文件(我这里命名为:一笔画游戏.html)和JavaScript脚本文件 (我这里命名为:game.js),我这里将这两个文件放在同一文件夹中。

html文件“一笔画游戏.html”源码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>一笔画游戏</title>
    <style>
        canvas {
            border: 1px solid black;
        }
    </style>
</head>
<body>
    <canvas id="gameCanvas" width="300" height="300"></canvas>
    <div>
        <button onclick="startGame('easy')">简单</button>
        <button onclick="startGame('medium')">中等</button>
        <button onclick="startGame('hard')">困难</button>
        <br> 
        连接线段时,必须按照一定的顺序来连接点。
        <br> 
        两点间连线方法:从一个点按下鼠标左键拖动到释放。
    </div>
    <script src="game.js"></script>
</body>
</html>

JavaScript脚本文件 game.js源码如下

// game.js

let canvas = document.getElementById("gameCanvas");
let ctx = canvas.getContext("2d");

let isDrawing = false;
let startPoint = null;
let lastPointId = null;

let presetPoints = [];
let presetLines = [];
let userLines = [];

const levels = {
    easy: {
        points: [
            {id: 1, x: 50, y: 50}, 
            {id: 2, x: 150, y: 50}, 
            {id: 3, x: 150, y: 150},
            {id: 4, x: 50, y: 150}
        ],
        lines: [
            {start: 1, end: 2}, 
            {start: 2, end: 3}, 
            {start: 3, end: 4},
            {start: 4, end: 1},
            {start: 2, end: 4}
        ]
    },
    medium: {
        points: [
            {id: 1, x: 50, y: 100}, 
            {id: 2, x: 150, y: 100}, 
            {id: 3, x: 250, y: 100}, 
            {id: 4, x: 100, y: 200}, 
            {id: 5, x: 200, y: 200}
        ],
        lines: [
            {start: 1, end: 2}, 
            {start: 2, end: 3}, 
            {start: 1, end: 4}, 
            {start: 2, end: 5}, 
            {start: 3, end: 5}, 
            {start: 4, end: 5}
        ]
    },
    hard: {
        points: [
            {id: 1, x: 50, y: 50}, 
            {id: 2, x: 150, y: 50}, 
            {id: 3, x: 250, y: 50}, 
            {id: 4, x: 50, y: 150}, 
            {id: 5, x: 150, y: 150}, 
            {id: 6, x: 250, y: 150}, 
            {id: 7, x: 50, y: 250}, 
            {id: 8, x: 150, y: 250}, 
            {id: 9, x: 250, y: 250}
        ],
        lines: [
            {start: 1, end: 2}, 
            {start: 2, end: 3}, 
            {start: 1, end: 4}, 
            //{start: 2, end: 5}, 
            {start: 3, end: 6}, 
            {start: 4, end: 5}, 
            //{start: 5, end: 6}, 
            {start: 4, end: 7}, 
            {start: 5, end: 8}, 
            {start: 6, end: 9}, 
            {start: 7, end: 8}, 
            {start: 8, end: 9}
        ]
    }
};

function startGame(difficulty) {
    const level = levels[difficulty];
    if (!level) {
        console.error('未知难度级别');
        return;
    }
    
    presetPoints = level.points;
    presetLines = level.lines;
    userLines = [];
    lastPointId = null;
    draw();
}

canvas.addEventListener("mousedown", (e) => {
    isDrawing = true;
    startPoint = getPointFromMouseEvent(e);
});

canvas.addEventListener("mouseup", (e) => {
    if (!isDrawing || !startPoint) return;
    let endPoint = getPointFromMouseEvent(e);
    if (endPoint && startPoint.id !== endPoint.id) {
        if (lastPointId === null || lastPointId === startPoint.id) {
            if (isPresetLine(startPoint.id, endPoint.id)) {
                userLines.push({start: startPoint, end: endPoint});
                lastPointId = endPoint.id;
                draw();
            } else {
                alert("不能绘制原图中不存在的线段。");
            }
        } else {
            alert("必须按顺序连接点。");
        }
    }
    isDrawing = false;
});

function getPointFromMouseEvent(e) {
    const rect = canvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    return presetPoints.find(p => Math.sqrt((p.x - x) ** 2 + (p.y - y) ** 2) < 10);
}

function isPresetLine(startId, endId) {
    return presetLines.some(line => (line.start === startId && line.end === endId) || (line.start === endId && line.end === startId));
}

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    // 绘制预设线段
    presetLines.forEach(line => {
        let start = presetPoints.find(p => p.id === line.start);
        let end = presetPoints.find(p => p.id === line.end);
        drawLine(start, end, 'black');
    });
    
    // 绘制用户线段
    userLines.forEach(line => {
        drawLine(line.start, line.end, 'red');
    });
    
    // 绘制点
    presetPoints.forEach(point => {
        drawPoint(point);
    });

    // 检查胜利条件
    if (checkWin()) {
        alert("恭喜,你完成了这个难度级别的游戏!");
    }
}

function checkWin() {
    if (userLines.length !== presetLines.length) {
        return false;
    }

    // 检查每个用户线段是否匹配预设线段
    for (let userLine of userLines) {
        const startId = userLine.start.id;
        const endId = userLine.end.id;
        const match = presetLines.some(line => 
            (line.start === startId && line.end === endId) || 
            (line.start === endId && line.end === startId)
        );
        if (!match) {
            return false;
        }
    }

    return true;
}

function drawLine(start, end, color) {
    ctx.beginPath();
    ctx.moveTo(start.x, start.y);
    ctx.lineTo(end.x, end.y);
    ctx.strokeStyle = color;
    ctx.stroke();
}

function drawPoint(point) {
    ctx.beginPath();
    ctx.arc(point.x, point.y, 5, 0, 2 * Math.PI);
    ctx.fillStyle = 'blue';
    ctx.fill();
}

// 初始化游戏
startGame('easy');

说明,定义不同难度的关卡数据:

const levels = {
    easy: {
        // 定义简单难度的点和线段
    },
    medium: {
        // 定义中等难度的点和线段
    },
    hard: {
        // 定义高难度的点和线段
    }
};

这些点的 x 和 y 坐标是基于画布的尺寸和布局预设的。你可能需要根据你的具体实现调整这些坐标值,以确保点和线在你的游戏界面中正确显示。此外,这些关卡设计仅作为示例,你可以根据需要调整点和线的数量及布局,创造出更多不同难度的关卡。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1525190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为OD机试 - 单词搜索,找到它 - 回溯(Java 2024 C卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述1、输入2、输出3、说明 四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&a…

Tiktok/抖音旋转验证码识别代码

一、引言 在数字世界的飞速发展中&#xff0c;安全防护成为了一个不容忽视的课题。Tiktok/抖音&#xff0c;作为全球最大的短视频平台之一&#xff0c;每天都有数以亿计的用户活跃在其平台上。为了保护用户的账号安全&#xff0c;Tiktok/抖音引入了一种名为“旋转验证码”的安…

比TODESK好用的软件

比ToDesk更好用的软件&#xff1a;探索远程桌面的新选择 在远程桌面控制领域&#xff0c;ToDesk无疑是一款广受欢迎的软件。然而&#xff0c;随着技术的不断进步&#xff0c;市场上涌现出许多新的竞争者&#xff0c;它们在功能、性能和使用体验上都可能超越ToDesk。本文将介绍…

C语言向C++过渡的基础知识(三)

目录 auto类型变量&#xff08;C11标准支持&#xff09; auto关键字介绍 auto关键字的使用 auto关键字基本使用 auto关键字配合指针和引用 auto关键字不可以推导的场景 基于范围的for循环&#xff08;C11标准支持&#xff09; 基于范围的for循环基础使用 基于范围的fo…

语音识别:whisper部署服务器(远程访问,语音实时识别文字)

Whisper是OpenAI于2022年发布的一个开源深度学习模型&#xff0c;专门用于语音识别任务。它能够将音频转换成文字&#xff0c;支持多种语言的识别&#xff0c;包括但不限于英语、中文、西班牙语等。Whisper模型的特点是它在多种不同的音频条件下&#xff08;如不同的背景噪声水…

【Linux杂货铺】进程的基本概念

目录 &#x1f308;前言&#x1f308; &#x1f4c1;进程的概念 &#x1f4c2;描述进程-PCB &#x1f4c2; 查看进程 &#x1f4c2; 查看正在运行的程序 &#x1f4c2;杀死进程 &#x1f4c2;通过系统调用获取进程标识符 &#x1f4c2;通过系统调用创建进程 &#x1f…

HCIA——TCP协议详解

目录 1、TCP概念及协议头部格式 1.1TCP特点 1.2TCP协议协议头部格式 1.3字段进行介绍 1.3.1源端口和目的端口 1.3.2序号(seq) 1.3.3确认序号(ack) 1.3.4数据偏移 1.3.5标志位 1.3.6窗口 1.3.7校验和 1.3.8紧急指针 2、TCP的可靠性 2.1 TCP可靠性的保障 2.2排序机…

论文阅读_参数微调_P-tuning_v2

1 P-Tuning PLAINTEXT 1 2 3 4 5 6 7英文名称: GPT Understands, Too 中文名称: GPT也懂 链接: https://arxiv.org/abs/2103.10385 作者: Xiao Liu, Yanan Zheng, Zhengxiao Du, Ming Ding, Yujie Qian, Zhilin Yang, Jie Tang 机构: 清华大学, 麻省理工学院 日期: 2021-03-18…

unityprotobuf自动生成C#

Release Protocol Buffers v3.19.4 protocolbuffers/protobuf GitHub 导入Source code 里面的 csharp/src/Google.Protobuf 进入Unity 拷贝其他版本的 System.Runtime.CompilerServices.Unsafe进入工程 使用protoc-3.19.4-win32 里面的exe去编译proto文件为C# using Sys…

软件测试相关内容第四弹 -- 测试用例与测试分类

写在前&#xff1a;我们已经掌握了关于软件测试的相关内容&#xff0c;知道了基本的测试过程&#xff0c;在做了一段时间的基础测试&#xff0c;熟悉了相关的业务后&#xff0c;测试人员会进行测试用例的编写&#xff0c;在日常测试中&#xff0c;也需要补充测试用例到现有的案…

HCIP —— 交换 (VLAN)

VLAN --- 虚拟局域网 在 HCIA 中 &#xff0c;已经学过交换机的一些基础配置&#xff0c;下面进行回顾一些简单的内容。 1.创建VLAN VLAN ID --- 区别和标识不同的VLAN 使用范围&#xff1a;0-4095 &#xff0c; 由12位二进制构成。 0 和 4095 作为 保留的VLAN。 …

静默安装OGG21.3微服务版本FOR ORACLE版本

静默安装OGG21.3微服务版本FOR ORACLE版本 silent install ogg21.3 for oracle 某度找来找去都没有找到一份可靠的静默安装OGG21.3微服务版本的案例&#xff0c;特别难受&#xff0c;为此将自己静默安装的步骤一步步贴出来分享给大家&#xff0c;请指点&#xff0c;谢谢。 至…

【生态适配】亚信安慧AntDB数据库与龙芯3C5000L完成兼容互认

日前&#xff0c;亚信安慧AntDB数据库系统V6.2在龙芯3C5000L平台上完成兼容性测试&#xff0c;功能与稳定性良好&#xff0c;被授予龙架构兼容互认证书。 图1&#xff1a;产品兼容性证明 随着“互联网”的纵深发展&#xff0c;数字技术创新成果与经济社会各领域深度融合&#…

电玩体验店怎么计时,佳易王ps5计时计费管理控制系统操作教程

电玩体验店怎么计时&#xff0c;佳易王ps5计时计费管理控制系统操作教程 一、前言 以下软件操作教程以 佳易王电玩计时计费管理系统软件V17.9为例说明 件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、电玩体验馆管理软件在计时的同时可以设置定时提醒&…

Java两周半速成之路(第十六天)

一、网络编程 1.概述&#xff1a; 就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换 2.网络模型 3.网络参考模型图 4.网络通信三要素 4.1IP地址 InetAddress类的使用&#xff1a; 注意&#xff1a;通过API查看&#xff0c;此类没有构造方法&#xff0c;如…

Spring Boot Starter: 快速简明地创建Spring应用

Spring Boot Starter是Spring Boot的核心功能之一&#xff0c;它帮助开发人员快速简明地创建、配置和运行Spring应用。在本文中&#xff0c;我们将详细介绍Spring Boot Starter以及如何使用它创建一个Spring Boot应用。 文章目录 什么是Spring Boot Starter?为何使用Spring B…

jetson nano——编译一些包的网址导航,pyside2,qt(持续更新)

目录 1.PySide2下载地址2.tesserocr下载地址3.Qt下载地址4.OpenSSL官网5.latex编译器下载地址5.1MikTex5.2TeX Live 1.PySide2下载地址 https://download.qt.io/official_releases/QtForPython/pyside2/ 如下图&#xff1a; 2.tesserocr下载地址 https://github.com/simonflue…

【送书福利第五期】:ARM汇编与逆向工程

文章目录 &#x1f4d1;前言一、ARM汇编与逆向工程1.1 书封面1.2 内容概括1.3 目录 二、作者简介三、译者介绍&#x1f324;️、粉丝福利 &#x1f4d1;前言 与传统的CISC&#xff08;Complex Instruction Set Computer&#xff0c;复杂指令集计算机&#xff09;架构相比&#…

进入docker容器中安装软件失败解,国外源慢,时间不同步,执行命令权限不够等问题解决办法

进入docker容器中安装软件失败解&#xff0c;时间不同步, 国外源慢&#xff0c;执行命令权限不够 等问题解决办法 首先我进入docker容器中&#xff0c;为了安装一个软件&#xff0c;引出了很多报错问题&#xff0c;报错如下&#xff1a; 1、无法用 ifconfig 或者 ip addr 的方…

Ansible自动化运维Inventory与Ad-Hoc

前言 自动化运维是指利用自动化工具和技术来简化、自动化和优化IT基础设施的管理和运维过程&#xff0c;从而提高效率、降低成本&#xff0c;并减少人为错误。在当今复杂的IT环境中&#xff0c;自动化运维已经成为许多组织和企业提高生产力和保证系统稳定性的重要手段。Ansibl…