Pull to refresh

Ханойская башня на JavaScript (30 строк кода)

В честь недели 30-строчных игрушек на JavaScript решил показать своё некогда творение — ханойская башня с различными уровнями сложности и анимацией. Итак, встречайте:

image

image
image

На самом деле я был лукавым и 30-и строк кода здесь нет, поскольку помимо JS кода использовались еще html-разметка и css-стили, а это при привидении в однострочный вид в любом случае займет еще одну строку (на деле это еще +30строк html и +122строки css).

Да и изначально JavaScript код умещался тоже в 120 строк кода, поэтому заголовок должен выглядеть немного иначе:
«Из 120 в 30»

Но после получаса раздумий, еще часа уговоров совести сделать ужасное и, в конечном итоге, минут 20 говнокодинга оптимизации кода был получен результат. Кому не терпится, то вот ссылка на jsfidle.

Особенности:

  • Несколько уровней сложности;
  • Оповещение о неправильном ходе;
  • Минимальная, но приятная анимация;
  • Сделано без абсолютного позиционирования и canvas. Если посмотреть на другие реализации этой игры на js, то многие очень любят позиционировать блоки с помощью абсолютных значений. Но не Я. Я не люблю абсолютного позиционирования, поэтому Я просто повернул игровую область на 180 градусов и использовал стандартное блочное позиционирование;
  • Использованные библиотеки — нет таких;
  • Нарисована в стиле минималистический модерн с пастельными(!) цветами.


Код

оптимизированный JavaScript:

function hanoiInit(difficulty){"use strict";
    var addClass = function (o,c){o.className = (o.className + " " + c).replace(/\s+/g, " ").replace(/(^ | $)/g, "");}
    var removeClass = function (o,c){o.className = o.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)", "g"), "$1").replace(/\s+/g, " ").replace(/(^ | $)/g, "");}
    var hasClass = function (o,c) {return  (new RegExp("(^|\\s)" + c + "(\\s|$)", "g")).test(o.className);}
    var redAlert = function (){document.querySelector(".hh-cont").className += " alert";setTimeout(function(){removeClass(document.querySelector(".hh-cont"), "alert");},200);}
    var colors = ["#82E9FF", "#BB8BFF","#FF695C","#FFF560","#80E845","#00FFB7","#E84018"], discs = {}, winornot={"1":false, "2":false,"3":true};
    (function generateCols(){document.querySelector(".ch-dif").style.top = -400+"px"; var i, firstCol = document.querySelectorAll(".l-third")[0];
        for(i=0; i<difficulty; i++){discs[i]=new Disk(256-(256/difficulty)*i);discs[i].insert(firstCol, i);}}());
    function addEvent(elem, evType, fn) {if (elem.addEventListener) elem.addEventListener(evType, fn, false);
        else if (elem.attachEvent) elem.attachEvent('on' + evType, fn); else elem['on' + evType] = fn;}
    function Disk(r){var that = this; this.r=r, this.node = createDisc(r);
        function createDisc(l){var nElem = document.createElement('div');
            nElem.innerHTML ='', nElem.style.width = r+"px", nElem.style.backgroundColor = colors.pop(), nElem.className = "hh-disc", nElem.returnWidth = parseInt(r);
            return nElem;}
        this.insert = function(where, i){if(i || i===0) that.node.elemId=i;
            if(where.children.length===0) where.appendChild(that.node);
            else if(where.children[where.children.length-1].returnWidth>that.node.returnWidth) where.appendChild(that.node);
            else document.querySelector(".hh-cont").className += " alert";
            setTimeout(function(){removeClass(document.querySelector(".hh-cont"), "alert");},200);
            removeClass(that.node, "active");}}
    addEvent(document.querySelector(".hh-cont"), "click", function(e){
        var target = e.srcElement || e.target, nodeId;
        if(hasClass(target,"hh-disc")) target = target.parentNode;
        if(!document.querySelector(".active")) { if(target.children.length===0) return false;addClass(target.children[target.children.length-1], "active");
        }else{nodeId = document.querySelector(".active").elemId;
            discs[nodeId].insert(target);
            if(!winornot["1"]&&document.querySelector(".l-first").children.length==difficulty) winornot["1"] = true;
            if(!winornot["2"]&&document.querySelector(".l-second").children.length==difficulty) winornot["2"] = true;
            if( winornot["1"] && winornot["2"] ) document.querySelector(".win").style.top=0;
        }});}


JavaScript до оптимизации
window.onload = function() {
    document.querySelector(".o-easy").onclick = function(){hanoiInit(3)};
    document.querySelector(".o-medium").onclick = function(){hanoiInit(5)};
    document.querySelector(".o-hard").onclick = function(){hanoiInit(7)};
}
function hanoiInit(dif){
    "use strict";
    var colors = ["#82E9FF", "#BB8BFF","#FF695C","#FFF560","#80E845","#00FFB7","#E84018"];
    var difficulty = parseInt(dif);
    var  discs = {};
    var winornot={"1":false, "2":false,"3":true};

    (function generateCols(){
        document.querySelector(".o-easy").onclick = function(){};
        document.querySelector(".o-medium").onclick = function(){};
        document.querySelector(".o-hard").onclick = function(){};
        document.querySelector(".ch-dif").style.top = -400+"px";
        var i;
        var firstCol = document.querySelectorAll(".l-third")[0];

        for(i=0; i<difficulty; i++){
            discs[i]=new Disk(256-(256/difficulty)*i);
            discs[i].insert(firstCol, i);
        }
    }());

    function addEvent(elem, evType, fn) {
        if (elem.addEventListener) {
            elem.addEventListener(evType, fn, false);
        }
        else if (elem.attachEvent) {
            elem.attachEvent('on' + evType, fn);
        }
        else {
            elem['on' + evType] = fn;
        }
    }

    function addClass(o, c){
        var re = new RegExp("(^|\\s)" + c + "(\\s|$)", "g");if (re.test(o.className)) return;
        o.className = (o.className + " " + c).replace(/\s+/g, " ").replace(/(^ | $)/g, "");
    }

    function removeClass(o, c){
        var re = new RegExp("(^|\\s)" + c + "(\\s|$)", "g");
        o.className = o.className.replace(re, "$1").replace(/\s+/g, " ").replace(/(^ | $)/g, "");
    }

    function hasClass(o,c) {
        var regexp =  new RegExp("(^|\\s)" + c + "(\\s|$)", "g");
        return  regexp.test(o.className);
    }

    function redAlert(){
     var hh = document.querySelector(".hh-cont");
        hh.className += " alert";
        setTimeout(function(){removeClass(hh, "alert");},200);
    }

    function Disk(r){
        var that = this;
        this.r=r;
        this.node = createDisc(r);

        function createDisc(l){
            console.log(l);
            var nElem = document.createElement('div');
            nElem.innerHTML ='';
            nElem.style.width = r+"px";
            nElem.style.backgroundColor = colors.pop();
            nElem.className = "hh-disc";
            nElem.returnWidth = parseInt(r);
            return nElem;
        }

        this.insert = function(where, i){
            if(i || i===0) that.node.elemId=i;
            if(where.children.length===0){
                where.appendChild(that.node);
            }else{
                if(where.children[0].returnWidth>that.node.returnWidth){
                    where.appendChild(that.node);
                }else{
                    redAlert();
                }
            }
            removeClass(that.node, "active");
        }
    }

    addEvent(document.querySelector(".hh-cont"), "click", function(e){
        var target = e.srcElement || e.target;
        if(hasClass(target,"hh-disc")) target = target.parentNode;
        var nodeId;
        if(!document.querySelector(".active")) {
            if(target.children.length===0) return false;
            addClass(target.children[target.children.length-1], "active");
        }else{
            nodeId = document.querySelector(".active").elemId;
            discs[nodeId].insert(target);
            checkWinOrNot();
        }
    });

    function checkWinOrNot(){
        if(!winornot["1"]){
            if(document.querySelector(".l-first").children.length==difficulty){
                winornot["1"] = true;
            }
        }
        if(!winornot["2"]){
            if(document.querySelector(".l-second").children.length==difficulty){
                winornot["2"] = true;
            }
        }
        if( winornot["1"] && winornot["2"] ){
            document.querySelector(".win").style.top=0;
        }
    }
}



Заключение

Создатель гоночка на JavaScript (30 строк кода) пользователь agegorin написал:
Ребята, занимайтесь нормальным программированием.

Я не соглашусь с ним. Но соглашусь с его благодарностями к тем, кто начал эту неделю ненормального программирования и кто её поддержал. И от себя пожелаю:
Ребята, будьте ненормальными программистами!
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.