【休闲益智】【HTML】我的数独我做主

简介: 【休闲益智】【HTML】我的数独我做主

前言


借鉴了数独参考资料:JavaScript九宫格数独生成算法


在原来的基础上进行了改造,完成了逆数独~ (即我的数独我做主~)


《我的数独我做主》:是一款pc端单机html小休闲益智游戏;需要键盘和鼠标搭配,自己制作数独,自己解数独~


微信截图_20220520173749.png


一、游戏介绍与规则


技术介绍

css + jq


游戏名称

《我的数独我做主》


游戏规则

随心所欲,自己的数独自己做主;想怎么分配数字就怎么分配数字。(没有限制,任由发挥,可以创建空的数独,再去自己填补完整的数独。也可以直接创建完整的数独,再点击解答~)


1.建数独

输入数字到对应位置,创建数独。


2.解数独

完成数独创建,再去解数独。


3.完成

流程完成会出现有显示创建的完成时间、解答的完成时间~


二、大体设计与代码讲解


大体设计


先“画”表格,往表格里填写数字,点击创建数独;数独判断是否成立;成立即为创建数独成功;开行进行解答数独,填写正确数独;点击完成,判断是否解对,对了即可胜利!


完整代码


/**
 * author:ls
 * email:liusaint@gmail.com
 * date:2016年4月9日
 * 
 * update: nfz
 * updateDate:2022年4月24日
 */
function SD() {
    this.sdArr = []; //生成的数独数组  
    this.errorArr = []; //错误的格子。
    this.blankNum = 81; //空白格子数量 
    this.backupSdArr = []; //数独数组备份。
    this.createTime = 1076275213; // 开始创建时间
    this.createEndTime = 1076275213; // 创建完成时间
}
SD.prototype = {
    constructor: SD,
    // 判断是否为数独
    isSudoku: function() {
        var backupSdArr = this.backupSdArr;
        // console.log(this.sdArr)
        var board = [];
        for (var i = 1; i < 10; i++) {
            var b = [];
            for (var j = 0; j < 9; j++) {
                if (backupSdArr[i * 10 + j + 1] == undefined) {
                    b[j] = "";
                } else {
                    b[j] = backupSdArr[i * 10 + j + 1];
                }
            }
            board[i - 1] = b;
        }
        // console.log(board)
        for (let i = 0; i < 9; i++) {
            let col = new Set()
            let row = new Set()
            for (let j = 0; j < 9; j++) {
                // console.log(board[i][j] )
                if (board[i][j] != "") { //判断行
                    if (!row.has(board[i][j])) {
                        row.add(board[i][j])
                    } else {
                        return false
                    }
                }
                if (board[j][i] != "") { //判断列
                    if (!col.has(board[j][i])) {
                        col.add(board[j][i])
                    } else {
                        return false
                    }
                }
            }
            let block = new Set()
            let x = parseInt(i / 3) * 3 //关键是找到块的x,y坐标
            let y = i % 3 * 3
            for (let k = 0; k < 9; k++) {
                if (board[x][y] != "") { //判断块
                    if (!block.has(board[x][y])) {
                        block.add(board[x][y])
                    } else {
                        return false
                    }
                }
                y++
                if ((k + 1) % 3 == 0) { //第4个换行
                    x += 1
                    y -= 3
                }
            }
        }
        return true;
    },
    createSudoku: function() {
        // console.log(this.sdArr)
        this.getInputVals();
        // console.log(this.backupSdArr)
        // console.log(this.sdArr)
        var isFlag = this.isSudoku();
        if (!isFlag) { // 不符合数独
            alert("当前数独不符合,请检查~");
            return;
        }
        this.createEndTime = new Date().getTime();
        var createShow = document.getElementById("createShow");
        var runningShow = document.getElementById("runningShow");
        createShow.style.display = "none";
        runningShow.style.display = "block";
    },
    init: function() {
        this.createDoms();
        this.drawCells();
        this.createBlank(this.blankNum);
        this.createBlankCells();
        this.createTime = new Date().getTime(); // 创建时间
    },
    reset: function() {
        //重置程序。
        this.errorArr = [];
        $(".sdspan").removeClass('bg_red blankCell');
        this.createSdArr();
        $(".sdspan[contenteditable=true]").prop('contenteditable', false);
        this.drawCells();
        this.createBlank(this.blankNum);
        this.createBlankCells();
        var createShow = document.getElementById("createShow");
        var runningShow = document.getElementById("runningShow");
        createShow.style.display = "block";
        runningShow.style.display = "none";
    },
    createSdArr: function() {
        //生成数独数组。
        var that = this;
        try {
            this.sdArr = [];
            this.setThird(2, 2);
            this.setThird(5, 5);
            this.setThird(8, 8);
            var allNum = [1, 2, 3, 4, 5, 6, 7, 8, 9];
            outerfor:
                for (var i = 1; i <= 9; i++) {
                    innerfor: for (var j = 1; j <= 9; j++) {
                        if (this.sdArr[parseInt(i + '' + j)]) {
                            continue innerfor;
                        }
                        var XArr = this.getXArr(j, this.sdArr);
                        var YArr = this.getYArr(i, this.sdArr);
                        var thArr = this.getThArr(i, j, this.sdArr);
                        var arr = getConnect(getConnect(XArr, YArr), thArr);
                        var ableArr = arrMinus(allNum, arr);
                        if (ableArr.length == 0) {
                            this.createSdArr();
                            return;
                            break outerfor;
                        }
                        var item;
                        //如果生成的重复了就重新生成。
                        do {
                            item = ableArr[getRandom(ableArr.length) - 1];
                        } while (($.inArray(item, arr) > -1));
                        this.sdArr[parseInt(i + '' + j)] = item;
                    }
                }
            this.backupSdArr = this.sdArr.slice();
        } catch (e) {
            //如果因为超出浏览器的栈限制出错,就重新运行。
            that.createSdArr();
        }
    },
    getXArr: function(j, sdArr) {
        //获取所在行的值。
        var arr = [];
        for (var a = 1; a <= 9; a++) {
            if (this.sdArr[parseInt(a + "" + j)]) {
                arr.push(sdArr[parseInt(a + "" + j)])
            }
        }
        return arr;
    },
    getYArr: function(i, sdArr) {
        //获取所在列的值。
        var arr = [];
        for (var a = 1; a <= 9; a++) {
            if (sdArr[parseInt(i + '' + a)]) {
                arr.push(sdArr[parseInt(i + '' + a)])
            }
        }
        return arr;
    },
    getThArr: function(i, j, sdArr) {
        //获取所在三宫格的值。
        var arr = [];
        var cenNum = this.getTh(i, j);
        var thIndexArr = [cenNum - 11, cenNum - 1, cenNum + 9, cenNum - 10, cenNum, cenNum + 10, cenNum - 9, cenNum + 1, cenNum + 11];
        for (var a = 0; a < 9; a++) {
            if (sdArr[thIndexArr[a]]) {
                arr.push(sdArr[thIndexArr[a]]);
            }
        }
        return arr;
    },
    getTh: function(i, j) {
        //获取所在三宫格的中间位坐标。
        var cenArr = [22, 52, 82, 25, 55, 85, 28, 58, 88];
        var index = (Math.ceil(j / 3) - 1) * 3 + Math.ceil(i / 3) - 1;
        var cenNum = cenArr[index];
        return cenNum;
    },
    setThird: function(i, j) {
        //为对角线上的三个三宫格随机生成。
        var numArr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
        var sortedNumArr = numArr.sort(function() { return Math.random() - 0.5 > 0 ? -1 : 1 });
        var cenNum = parseInt(i + '' + j);
        var thIndexArr = [cenNum - 11, cenNum - 1, cenNum + 9, cenNum - 10, cenNum, cenNum + 10, cenNum - 9, cenNum + 1, cenNum + 11];
        for (var a = 0; a < 9; a++) {
            this.sdArr[thIndexArr[a]] = sortedNumArr[a];
        }
    },
    drawCells: function() {
        //将生成的数组填写到九宫格
        for (var j = 1; j <= 9; j++) {
            for (var i = 1; i <= 9; i++) {
                $(".sdli").eq(j - 1).find(".sdspan").eq(i - 1).html(this.sdArr[parseInt(i + '' + j)]);
            }
        }
    },
    createBlank: function(num) {
        //生成指定数量的空白格子的坐标。
        var blankArr = [];
        var numArr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
        var item;
        for (var a = 0; a < num; a++) {
            do {
                item = parseInt(numArr[getRandom(9) - 1] + '' + numArr[getRandom(9) - 1]);
            } while ($.inArray(item, blankArr) > -1);
            blankArr.push(item);
        }
        this.blankArr = blankArr;
    },
    createBlankCells: function() {
        //在创建好的数独中去除一部分格子的值,给用户自己填写。把对应格子变成可编辑,并添加事件。
        var blankArr = this.blankArr,
            len = this.blankArr.length,
            x, y, dom;
        for (var i = 0; i < len; i++) {
            x = parseInt(blankArr[i] / 10);
            y = blankArr[i] % 10;
            dom = $(".sdli").eq(y - 1).find(".sdspan").eq(x - 1);
            dom.attr('contenteditable', true).html('').addClass('blankCell');
            this.backupSdArr[blankArr[i]] = undefined;
        }
        $(".sdspan[contenteditable=true]").keyup(function(event) {
            var val = $(this).html();
            var reStr = /^[1-9]{1}$/;
            if (!reStr.test(val)) {
                $(this).html('');
            };
        });
    },
    checkRes: function() {
        //检测用户输入结果。检测前将输入加入数组。检测单个的时候将这一个的值缓存起来并从数组中删除,检测结束在赋值回去。
        var blankArr = this.blankArr,
            len = this.blankArr.length,
            x, y, dom, done, temp;
        this.getInputVals();
        this.errorArr.length = 0;
        for (var i = 0; i < len; i++) {
            x = parseInt(blankArr[i] / 10);
            y = blankArr[i] % 10;
            temp = this.backupSdArr[blankArr[i]];
            this.backupSdArr[blankArr[i]] = undefined;
            this.checkCell(x, y);
            this.backupSdArr[blankArr[i]] = temp;
        }
        done = this.isAllInputed();
        if (this.errorArr.length == 0 && done) {
            var gameTime = ((new Date().getTime()) - this.createEndTime) / 1000;
            var createTime = (this.createEndTime - this.createTime) / 1000;
            alert('你胜利了!创建数独耗时:' + createTime + ';完成数独耗时:' + gameTime + "秒!");
            $(".bg_red").removeClass('bg_red');
        } else {
            if (!done) {
                alert("你没有完成游戏!");
            }
            this.showErrors();
        }
    },
    checkCell: function(i, j) {
        //检测一个格子中输入的值,在横竖宫里是否已存在。
        var index = parseInt(i + '' + j);
        var backupSdArr = this.backupSdArr;
        var XArr = this.getXArr(j, backupSdArr);
        var YArr = this.getYArr(i, backupSdArr);
        var thArr = this.getThArr(i, j, backupSdArr);
        var arr = getConnect(getConnect(XArr, YArr), thArr);
        var val = parseInt($(".sdli").eq(j - 1).find(".sdspan").eq(i - 1).html());
        if ($.inArray(val, arr) > -1) {
            this.errorArr.push(index);
        }
    },
    getInputVals: function() {
        //将用户输入的结果添加到数组中。
        var blankArr = this.blankArr,
            len = this.blankArr.length,
            i, x, y, dom, theval;
        for (i = 0; i < len; i++) {
            x = parseInt(blankArr[i] / 10);
            y = blankArr[i] % 10;
            dom = $(".sdli").eq(y - 1).find(".sdspan").eq(x - 1);
            theval = parseInt(dom.text()) || undefined;
            this.backupSdArr[blankArr[i]] = theval;
        }
    },
    isAllInputed: function() {
        //检测是否全部空格都有输入。
        var blankArr = this.blankArr,
            len = this.blankArr.length,
            i, x, y, dom;
        for (i = 0; i < len; i++) {
            x = parseInt(blankArr[i] / 10);
            y = blankArr[i] % 10;
            dom = $(".sdli").eq(y - 1).find(".sdspan").eq(x - 1);
            if (dom.text() == '') {
                return false
            }
        }
        return true;
    },
    showErrors: function() {
        //把错误显示出来。
        var errorArr = this.errorArr,
            len = this.errorArr.length,
            x, y, dom;
        $(".bg_red").removeClass('bg_red');
        for (var i = 0; i < len; i++) {
            x = parseInt(errorArr[i] / 10);
            y = errorArr[i] % 10;
            dom = $(".sdli").eq(y - 1).find(".sdspan").eq(x - 1);
            dom.addClass('bg_red');
        }
    },
    createDoms: function() {
        //生成九宫格。
        var html = '<ul class="sd clearfix">';
        String.prototype.times = String.prototype.times || function(n) { return (new Array(n + 1)).join(this); };
        html = html + ('<li class="sdli">' + '<span class="sdspan"></span>'.times(9) + '</li>').times(9) + '</ul>';
        $("#sudoku-body").prepend(html);
        for (var k = 0; k < 9; k++) {
            $(".sdli:eq(" + k + ") .sdspan").eq(2).addClass('br');
            $(".sdli:eq(" + k + ") .sdspan").eq(5).addClass('br');
            $(".sdli:eq(" + k + ") .sdspan").eq(3).addClass('bl');
            $(".sdli:eq(" + k + ") .sdspan").eq(6).addClass('bl');
        }
        $(".sdli:eq(2) .sdspan,.sdli:eq(5) .sdspan").addClass('bb');
        $(".sdli:eq(3) .sdspan,.sdli:eq(6) .sdspan").addClass('bt');
    }
}
//生成随机正整数
function getRandom(n) {
    return Math.floor(Math.random() * n + 1)
}
//两个简单数组的并集。
function getConnect(arr1, arr2) {
    var i, len = arr1.length,
        resArr = arr2.slice();
    for (i = 0; i < len; i++) {
        if ($.inArray(arr1[i], arr2) < 0) {
            resArr.push(arr1[i]);
        }
    }
    return resArr;
}
//两个简单数组差集,arr1为大数组
function arrMinus(arr1, arr2) {
    var resArr = [],
        len = arr1.length;
    for (var i = 0; i < len; i++) {
        if ($.inArray(arr1[i], arr2) < 0) {
            resArr.push(arr1[i]);
        }
    }
    return resArr;
}


三、仓库地址与体验地址


  这里代码片段展示(布局不会弄,太拉了~ alter 弹窗不支持,最好去网站体验~)


<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,user-scalable=no" />
  <title>我的数独我做主 - 掘金 - 南方者</title>
</head>
<body>
  <div style="text-align: center;">
    <h1>我的数独我做主</h1>by <a target="_blank" href="https://juejin.cn/user/2840793779295133/posts">掘金-南方者</a>
    <div id="sudoku-body">
      <div class="btn-group">
        <div style="display: block;" id="createShow">
          <button onclick="sd.createSudoku();">创建数独</button>
        </div>
        <div style="display: none;" id="runningShow">
          <button onclick="sd.checkRes();">完成</button>
          <button onclick="sd.reset();">重新开始</button>
        </div>
      </div>
    </div>
  </div>
</body>
</html>


  (没有做适配,很抱歉~ 手机端也不友好。献丑了~ )


  大家可以直接来笔者的网站来体验


  在线体验(pc端):体验传送门


  仓库地址:等建好活动GitHub的要求申请了,就给大家放~(着急想要的可以直接去扒我的网站~) 传送门


文章小尾巴


文章写作、模板、文章小尾巴可参考:《写作“小心思”》


  感谢你看到最后,最后再说两点~


  ①如果你持有不同的看法,欢迎你在文章下方进行留言、评论。

  ②如果对你有帮助,或者你认可的话,欢迎给个小点赞,支持一下~


  我是南方者,一个热爱计算机更热爱祖国的南方人。


  (文章内容仅供学习参考,如有侵权,非常抱歉,请立即联系作者删除。)



目录
相关文章
|
5天前
|
XML 前端开发 JavaScript
Html:CSS介绍
Html:CSS介绍
18 1
|
5天前
|
前端开发
Html:CSS的书写位置
Html:CSS的书写位置
14 0
|
1天前
|
前端开发 JavaScript 搜索推荐
打造个人博客网站:从零开始的HTML与CSS之旅
【9月更文挑战第31天】在这个数字时代,拥有一个个人博客网站是展示自我、分享知识和连接世界的重要方式。本文将引导你通过简单的HTML和CSS知识,一步步构建起你的在线空间。无论你是编程新手还是希望通过实践加深理解,这篇文章都将是你的理想指南。我们将探索基本概念,实现页面布局,并点缀以个性化样式,最终将静态页面转变为动态交互式网站。准备好了吗?让我们开始吧!
|
5天前
|
XML 前端开发 JavaScript
jQuery HTML / CSS 方法
jQuery HTML / CSS 方法
8 2
|
7天前
|
JavaScript 前端开发
JavaScript HTML DOM - 改变CSS
JavaScript HTML DOM - 改变CSS
15 4
|
4天前
|
前端开发 JavaScript
HTML+JavaScript+CSS DIY 分隔条splitter
HTML+JavaScript+CSS DIY 分隔条splitter
|
5天前
|
前端开发 数据安全/隐私保护 容器
HTML+CSS 水滴登录页
该代码实现了一个创意的水滴登录页面,包含一个水滴形状的登录框与两个按钮(忘记密码和注册)。登录框包括用户名、密码输入框及登录按钮。页面设计独特,采用渐变色与动态效果,增强了交互性和视觉美感。以下为关键实现步骤: - 重置默认样式。 - 设置页面背景颜色和尺寸。 - 定义登录表单容器的布局、位置和尺寸。 - 设置登录表单内容样式,包括3D效果和过渡动画。 - 创建伪元素增强水滴效果。 - 设定输入框容器和输入框样式。 - 为提交按钮、忘记密码和注册按钮设定特定样式,并添加悬停效果。
WK
|
7天前
|
存储 移动开发 前端开发
HTML5和CSS5有什么区别
HTML5和CSS5在网页设计中扮演不同角色。HTML5是超文本标记语言的第五版,通过新特性如实时更新、跨平台运行及更好的安全性等,定义网页内容和结构。尽管常说CSS5,实际最新的CSS版本包含多个模块如CSS Grid和Flexbox,主要用于控制网页布局和样式,提供强大的选择器、动画支持和响应式设计,与HTML5相辅相成,共同构建现代网页的基础架构。
WK
23 3
|
23天前
|
Web App开发 前端开发 JavaScript
HTML/CSS/JS学习笔记 Day3(HTML--网页标签 下)
HTML/CSS/JS学习笔记 Day3(HTML--网页标签 下)
下一篇
无影云桌面