zoukankan      html  css  js  c++  java
  • 使用JavaScript写的汉诺塔小游戏

    汉诺塔有些,是将A柱子上的盘子,借助B柱子,移动到C柱子,移动过程中要求,小盘子,必须放在大盘子上面。

    移动过程是采用递归调用的方式。

    程序运行界面:如下图:

    代码如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>汉诺塔</title>
    </head>
    <body>
    
    <h1>  汉诺塔 </h1>
    
    <canvas id = "canvas" width = 400, height = 300">
    
    
    </canvas>
    <div>
    <button id = "ps" onclick=" pStep()"> <=== Preview Step </button>
    <button id = "ns" onclick= "nStep()"> Next Step  ===> </button>
    </div>
    <script>
    
        function Stack(name) {
            this.dataStore = [];
            this.top = 0;
            this.name = name;
    
            this.push = push;
            this.pop = pop;
            this.peek = peek;
            this.clear = clear;
            this.length = length;
            this.get = get;
            this.drawDish = drawDish;
            this.myCloneobj= myCloneobj;
        }
    
        function push(ele) {
            this.dataStore[this.top ++] = ele;
        }
    
        function peek() {
            return this.dataStore[this.top - 1];
        }
    
        function pop() {
            return this.dataStore[-- this.top];
        }
    
        function clear() {
            this.top = 0;
        }
    
        function length() {
            return this.top;
        }
    
        function get(n){
            return this.dataStore[n-1];
        }
    
        function Block(size, location){
            this.size = size;
            this.location = location;
            this.setLocation = setLocation;
            this.getSize = getSize;
        }
    
        function setLocation(location)
        {
            this.location = location;
        }
    
        function getSize()
        {
            return this.size;
        }
    
        function drawDish(){
            var canvas = document.getElementById('canvas');
            if (canvas.getContext) {
                var ctx = canvas.getContext('2d');
                var width, height, layer, pole, xPos, yPos;
                //x基准点125, y基准点205
                //画一个柱子
                pole = this.name;
                n = this.length();
                b = new Block();
                console.log(" draw pole %d, length %d", pole , n);
                while (n>0){
                    b = this.get(n);
                    var size = b.size;
                    width = (size+2)*10;
                    height = 20;
                    layer = n - 1;
                    xPos = 125 + pole * 75 - width/2;
                    yPos = 205 - layer*25 - height/2 ;
    
                    roundedRect(ctx, xPos, yPos, width, height, 15);
                    ctx.fillStyle = "red";
                    ctx.fill();
                    n--;
                }
             }
        }
    
        function myCloneobj()
        {
            o = new Stack(this.name);
            console.log("clone pole %d", this.name);
            n = this.length();
            var i=1;
            while (i <= n)
            {
                var bt = this.get(i);
                var size = bt.size;
                var location = bt.location;
                b = new Block(size, location);
                o.push(b);
                console.log("insert new b size %d, location %d", size, location);
                i=i+1;
            }
            return o;
        }
    
        //深复制对象方法
        function cloneObj(obj) {
            //1
            var newJsonObj = {};
            newJsonObj = JSON.parse(JSON.stringify(obj));
            //2
            for (items in obj) {
                if (typeof obj[items] == "function" || typeof obj[items] == "undefined" || obj[items] instanceof RegExp) {
                    newJsonObj[items] = obj[items];
                }
            }
            //3
            var newObj = new obj.constructor;
            for (items in newJsonObj) {
                newObj[items] = newJsonObj[items]
            }
            return newObj;
        }
    
        //深复制对象方法
        var cloneObj2 = function (obj) {
            var newObj = {};
            if (obj instanceof Array) {
                newObj = [];
            }
            for (var key in obj) {
                var val = obj[key];
                //newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; //arguments.callee 在哪一个函数中运行,它就代表哪个函数, 一般用在匿名函数中。
                newObj[key] = typeof val === 'object' ? cloneObj2(val): val;
            }
            return newObj;
        };
    
        function roundedRect(ctx, x, y, width, height, radius){
              ctx.beginPath();
              ctx.moveTo(x, y + radius);
              ctx.lineTo(x, y + height - radius);
              ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
              ctx.lineTo(x + width - radius, y + height);
              ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
              ctx.lineTo(x + width, y + radius);
              ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
              ctx.lineTo(x + radius, y);
              ctx.quadraticCurveTo(x, y, x, y + radius);
              ctx.stroke();
        }
    
        function drawRect(){
              var canvas = document.getElementById('canvas');
              if (canvas.getContext) {
                var ctx = canvas.getContext('2d');
                ctx.fillStyle = "black";
                //画边框
                ctx.strokeRect(10, 10, 380, 280);
                //画左柱子
                ctx.fillRect(120, 100, 10, 120);
                ctx.fillRect(100, 220, 50, 20);
                //画中柱子
                ctx.fillRect(195, 100, 10, 120);
                ctx.fillRect(175, 220, 50, 20);
                //画右柱子
                ctx.fillRect(270, 100, 10, 120);
                ctx.fillRect(250, 220, 50, 20);
    
                //ctx.clearRect(125, 25, 10, 200);
                //ctx.strokeRect(50, 50, 50, 50);
              }
        }
    
        function drawText(str) {
            var canvas = document.getElementById('canvas');
            if (canvas.getContext){
                var ctx = canvas.getContext('2d');
                ctx.font = "16px serif";
                ctx.fillText(str, 20, 40);
            }
        }
    
        function clear(){
            var canvas = document.getElementById('canvas');
                if (canvas.getContext) {
                      var ctx = canvas.getContext('2d');
                      ctx.clearRect(20,20,360,260);
                      ctx.fillStyle = "black";
                }
        }
    
        function moveBlock(s1,s2){
            if(s1.length() > 0){
                if( (s2.length() == 0) || ((s2.length() > 0) && (s2.peek().size > s1.peek().size))){
                    b = s1.pop();
                    b.location = s2.name;
                    s2.push(b);
                    console.log("block %d move form %d to %d", b.size, s1.name, s2.name);
                    //如果是简单变量,会直接创建并复制,如果是类变量,特别是有复杂结构的类变量,只是复制一个地址。
                    process.push(s1.name);
                    process.push(s2.name);
                }
            }
        }
    
        function hanoi(n, a, b, c){
            if (n == 1){
                moveBlock(a, c);
            }
            else{
                hanoi(n - 1, a, c, b);
                moveBlock(a, c);
                hanoi(n - 1, b, a, c);
            }
        }
    
        var step = 0;
    
    
        function draw(a, b, c) {
            var str = " Hanota process ";
            var s = step + 1;
            str = str.concat( s.toString(), " steps");
            clear();
            drawText(str);
            drawRect();
            a.drawDish();
            b.drawDish();
            c.drawDish();
        }
    
        var level = 4;
    
        var process = [];
    
        var leftPole = new Stack(0);//0 - leftPole
        var middlePole = new Stack(1);//1 - middlePole
        var rightPole = new Stack(2);//2 - rightPole
    
        var i = level;
        while (i>0){
            var block = new Block(i, 0);
            leftPole.push(block);
            i--;
        }
    
        var leftPolebk = leftPole.myCloneobj();
        var middlePolebk = middlePole.myCloneobj();
        var rightPolebk = rightPole.myCloneobj();
    
        draw(leftPolebk, middlePolebk, rightPolebk);
    
        hanoi(level, leftPole, middlePole, rightPole);
    
    
        var n = level;
        var maxStep = 1;
        while (n>0) {
            maxStep = maxStep*2;
            n=n-1;
        }
        maxStep = maxStep - 1;
    
        function pStep()
        {
            step = step - 1;//退回上一个节点
    
            if (step >= 0){
                var p1 = process[step*2];
                var p2 = process[step*2+1];
                console.log(" step %d move from %d to %d ",step, p2, p1);
                if (p2 == 0)
                    var b = leftPolebk.pop();
                else if (p2==1)
                    b = middlePolebk.pop();
                else if (p2==2)
                    b = rightPolebk.pop();
                else
                    alert("wrong1");
    
                if (p1 == 0)
                    leftPolebk.push(b);
                else if (p1 == 1)
                    middlePolebk.push(b);
                else if (p1 == 2)
                    rightPolebk.push(b);
                else
                    alert("wrong2");
    
                draw(leftPolebk, middlePolebk, rightPolebk);
            }
            else
            {
                step=0
            }
        }
    
         function nStep()
        {
            if (step <= maxStep){
                var p1 = process[step*2];
                var p2 = process[step*2+1];
                console.log(" step %d move from %d to %d ",step, p1, p2);
                if (p1 == 0)
                    var b = leftPolebk.pop();
                else if (p1==1)
                    b = middlePolebk.pop();
                else if (p1==2)
                    b = rightPolebk.pop();
                else
                    return;
    
                if (p2 == 0)
                    leftPolebk.push(b);
                else if (p2 == 1)
                    middlePolebk.push(b);
                else if (p2 == 2)
                    rightPolebk.push(b);
                else
                    return;
    
                draw(leftPolebk, middlePolebk, rightPolebk);
    
                step = step + 1;//指向下一个节点
            }
        }
    
    </script>
    </body>
    </html>

    代码说明:

    1.  Canvas画布,画塔,盘子和移动说明文字

    2. 用Stack类,存放柱子信息,类是用堆栈来实现的, 类的名称属性,表示是哪一根柱子,左,中,还是右?

        function Stack(name) {
            this.dataStore = [];
            this.top = 0;
            this.name = name;

    3. 用Block类,存放盘子信息,size表示盘子的大小,Location标明盘子在那个柱子上。

      function Block(size, location){
            this.size = size;
            this.location = location;
            this.setLocation = setLocation;
            this.getSize = getSize;
        }

    4. 初始化时候,生成了3个盘子,3个柱子,并把3个盘子push到最左边的柱子上。

    5. 调用画图函数,画出背景信息,三个柱子信息。

    6. 调用递归函数,实现盘子移动

      function hanoi(n, a, b, c){
            if (n == 1){
                moveBlock(a, c);
            }
            else{
                hanoi(n - 1, a, c, b);
                moveBlock(a, c);
                hanoi(n - 1, b, a, c);
            }
        }  

    程序本身没有什么好说的,这里需要说明的是如何将移动的步骤保存下来。
    尝试了三种方法:
      1、在递归调用过程中,复制对象到一个数组中
      2、在递归调用中,将程序停下来
      3. 保存移动过程信息,到一个数组中

    由于JavaScript对于对象的复制,对于Obj,只复制一个链接。这样导致的结果,复制下来的对象,在数组中都是一样的。
    后来采用深度复制的方式,将obj中的obj,全部new来,但是这种方法,对于静态操作有效,在递归迭代过程中,第一层的还能复制,但是第二次以后的,都复制不了了。

    在递归调用过程中,尝试alert, promote, sleep方法,暂停程序的执行。但是这种方法,只对异步的操作有效(你给我消息,我相应),但是对于这种代码执行迭代过程,是卡不住的。

    最后只好采用一个数组,只记录移动盘子的原和目的地址信息。

    7. 移动过程演示

    因为递归调用过程,是不能暂停程序的。用了数组将盘子移动的过程录制下来。
    在演示过程中,读取录制的移动过程数据,让操作过程再执行一遍。

    这里克隆了三个对象,用于操作过程的演示。
        var leftPolebk = leftPole.myCloneobj();
        var middlePolebk = middlePole.myCloneobj();
        var rightPolebk = rightPole.myCloneobj();
    8. 

  • 相关阅读:
    销售类
    语法
    编辑技巧 word
    assert
    游戏摘录
    游戏类链接 财富导图
    读书笔记之C# delegate
    c# socket传输struct类型
    关于wcf中一些重要词语解释
    asp.net CROSS-PAGE POSTING
  • 原文地址:https://www.cnblogs.com/montai/p/13217191.html
Copyright © 2011-2022 走看看