Bird's Opening

Details

Author:Mark
Rating:
Difficulty:Beginner
Date:8th February 2021
Description:A trainer to help you learn and memorize the main lines and variations of Bird's opening.

Code

add("1.f4", "This is Bird's opening");
add("1.f4 Nf6 2.b4 g6 3.Nf3", "This is the Batavo-Polish Attack");
add("1.f4 Nh6", "This is the Horsefly Defense");
add("1.f4 g5", "This is the Hobbs Gambit");
add("1.f4 f5 2.e4 fxe4 3.Nc3 Nf6 4.g4", "This is the Swiss, Wagner-Zwitersch Gambit");

add("1.f4 e5", "This is From's Gambit");
add("1.f4 e5 2.fxe5 Nc6", "This is the Schlechter Gambit");
add("1.f4 e5 2.fxe5 d6 3.exd6 Bxd6 4.Nf3 Nh6 5.d4", "This is From's Gambit, Lipke Variation");
add("1.f4 e5 2.fxe5 d6 3.exd6 Bxd6 4.Nf3 g5", "This is From's Gambit, Lasker Variation");

add("1.f4 d5", "This is the Dutch Variation");
add("1.f4 d5 2.e3 Nf6 3.Nf3 c5", "This is the Dutch, Lasker Variation");
add("1.f4 d5 2.e3 Nf6 3.Nf3 Bg4", "This is the Dutch, Schlechter Gambit");
add("1.f4 d5 2.c4", "This is the Dutch, Mujannah-Sturm gambit");
add("1.f4 d5 2.d4", "This is the Dutch, Canard Variation");
add("1.f4 d5 2.d4 f5", "This is the Dutch, Double Duck Variation");

add("1.f4 d5 2.e4", "This is William's gambit");
add("1.f4 d5 2.e4 dxe4 3.d3", "This is the Prokofiev Gambit");

function add(line, name) {
    let dict = (Window.dict || (Window.dict = {}));
    let notes = (Window.notes || (Window.notes = {}));

    let split = parseLine(line);

    let temp = dict;

    for (let move of split) {
        if (temp[move] != undefined)
        {
            temp = temp[move];
        }
        else
        {
            temp[move] = {};
            temp = temp[move];
        }
    }

    temp.id = nextID();

    notes[temp.id] = name;
}

function nextID() {
    Window.nextInt = (Window.nextInt || 0) + 1;
    return Window.nextInt;
}

function parseLine(line) {
    line = line.replace("\r\n", " ");
    line = line.replace("  ", " ");

    let split = [];

    let regex = /(?:(\d+)(?:\.+|…)\s?)([^\s]+)(?:\s(\w[^\s\.…]+))?/g;
    let match = regex.exec(line);

    do {
        let moveIndex = parseInt(match[1]);

        //set it relative to 0
        moveIndex--;

        if (moveIndex * 2 > split.length + 1) {
            throw new Error();
        }

        split.push(match[2]);

        if (match[3] != undefined) {
            split.push(match[3]);
        }
    } while ((match = regex.exec(line)) !== null);

    return split;
}

function buildList(list, dict) {
    let notes = (Window.notes || (Window.notes = {}));

    let keys = Object.keys(dict);

    const index = keys.indexOf("id");
    if (index > -1) {
      keys.splice(index, 1);
    }

    switch (keys.length) {
        case 0:
            if (notes[dict.id] != undefined) {
                list.push({
                    info: notes[dict.id]
                });

                notes[dict.id] = undefined;
            }
            break;
        case 1:
            let key = keys[0];

            //we can add it directly
            list.push(key);

            if(notes[dict[key].id] != undefined) {
                list.push({
                    info: notes[dict[key].id]
                });

                notes[dict[key].id] = undefined;
            }

            //and send the same list through for the submoves
            buildList(list, dict[key]);
            break;
        default:
            for (let key of keys) {
                let subList = [];
                subList.push(key);

                if(notes[dict[key].id] != undefined) {
                    subList.push({
                        info: notes[dict[key].id]
                    });
                    
                    notes[dict[key].id] = undefined;
                }
        
                list.push(subList);
                buildList(subList, dict[key]);
            }
        break;
    }
}

function sortMoves(moves) {
    moves.sort(function(a, b) {
        let aSource = sourceOfSan(a);
        let bSource = sourceOfSan(b);

        if(aSource.substr(0, 1) != bSource.substr(0, 1)) {
            return aSource.charCodeAt(0) - bSource.charCodeAt(0);
        }

        if(aSource.substr(1, 1) != bSource.substr(1, 1)) {
            return bSource.charCodeAt(1) - aSource.charCodeAt(1);
        }

        let aDest = destOfSan(a);
        let bDest = destOfSan(b);

        if(aDest.substr(0, 1) != bDest.substr(0, 1)) {
            return aDest.charCodeAt(0) - bDest.charCodeAt(0);
        }

        if(aDest.substr(1, 1) != bDest.substr(1, 1)) {
            return bDest.charCodeAt(1) - aDest.charCodeAt(1);
        }

        return 0;
    });
}

let root = [];
buildList(root, Window.dict);

let targetMove = "";
onPieceClicked(function (squareName) {
    if (typeof targetMove == "string") {
        if (squareName == sourceOfSan(targetMove)) {
            return true;
        }
    } else if (Array.isArray(targetMove)) {
        for (let temp of targetMove) {
            if (squareName == sourceOfSan(temp)) {
                return true;
            }
        }
    } else {
        throw new Error();
    }
    if (targetMove == "") {
        return false;
    }

    if (squareName == sourceOfSan(targetMove)) {
        return true;
    }

    highlightSquareRed(squareName);
    return false;
});

onMoveAttempted(function (san) {
    if (typeof targetMove == "string") {
        if (san == targetMove) {
            current.index++;
            setTimeout(next, 50);
            return true;
        }
    } else if (Array.isArray(targetMove)) {
        for (let temp of targetMove) {
            if (san == temp) {
                for (let i = current.index; i < current.length; i++) {
                    if (current[i][0] == san) {
                        current[i].parent = current;
                        current = current[i];
                        current.index = 1;
                        break;
                    }
                }

                setTimeout(next, 50);
                return true;
            }
        }
    } else {
        throw new Error();
    }

    return false;
});

onNextClicked(function () {
    showNextButton(false);
    next();
});

function nextDelayed() {
    setTimeout(next, 500);
}

function onUndoClicked() {
    undo();

    if (current.index > 0) {
        current.index--;

        if (current.index > 0 && current[current.index].info != undefined) {
            current.index--;
        }
    }

    if (current.index == 0) {
        if (current.parent != undefined) {
            current = current.parent;
        } else {
            //showButton("undo", false);
        }
    }

    next();
}

let current = root;
current.index = 0;

let preInfo = "";

let flipped = false;
createButton("Flip", function () {
  blackAtTop(flipped);
  flipped = !flipped;
});

createButton("Undo", onUndoClicked);
showButton("Undo", false);
let undoButtonVisible = false;

function next() {
    let showUndoButton = current != root || current.index > 0;
    if (undoButtonVisible != showUndoButton) {
        showButton("Undo", showUndoButton);
        undoButtonVisible = showUndoButton;
    }

    if (preInfo != "") {
        preInfo += "<br><br>";
    }

    if (current.index >= current.length) {
        info(preInfo + "End of the line");
        preInfo = "";
        return;
    }

    let step = current[current.index];

    if (typeof step == "string") {
        if (isWhitesTurn()) {
            info(preInfo + "White to play <b>" + currentMoveNumber() + "." + step + "</b>");
        } else {
            info(preInfo + "Black to play <b>" + currentMoveNumber() + "..." + step + "</b>");
        }

        targetMove = step;
        preInfo = "";
    } else if (Array.isArray(step)) {
        ///assume that the rest of the steps in the current array are also arrays
        //i.e. different options from this point

        let possibleSteps = [];
        for (let i = current.index; i < current.length; i++) {
            //assume that the first step in every array is a string
            //otherwise it would be seperated out into an array at the previous level
            possibleSteps.push(current[i][0]);
        }

        sortMoves(possibleSteps);
        targetMove = possibleSteps;

        if (isWhitesTurn()) {
            possibleSteps = possibleSteps.map(x => "<li><b>" + currentMoveNumber() + "." + x + "</b></li>");
            info(preInfo + "White can play: <ul>" + possibleSteps.join("") + "</ul>");
        } else {
            possibleSteps = possibleSteps.map(x => "<li><b>" + currentMoveNumber() + "..." + x + "</b></li>");
            info(preInfo + "Black can play: <ul>" + possibleSteps.join("") + "</ul>");
        }

        preInfo = "";
    } else {
        if (step.info != undefined) {
            preInfo = step.info;
            current.index++;
            next();
        }
    }
}

next();