Caro-Kann Defense Trainer

Details

Author:Mark
Rating:
Difficulty:Beginner
Date:5th March 2021
Description:A trainer to help you learn and memorize the major variations of the Caro-Kann Defense.

Code

add("1.e4 c6", "This is the Caro-Kann Defense");
add("1.e4 c6 2.Bc4", "This is the Hillbilly Attack");
add("1.e4 c6 2.d3", "This is the Breyer Variation");
add("1.e4 c6 2.c4", "This is the Accelerated Panov Attack");
add("1.e4 c6 2.Nc3 d5 3.Qf3", "This is the Goldman Variation");
add("1.e4 c6 2.Nc3 d5 3.Nf3", "This is the Two Knights Variation");
add("1.e4 c6 2.Nc3 d5 3.Nf3 Bg4", "This is the Mindeno Variation");
add("1.e4 c6 2.Nc3 d5 3.Nf3 dxe4 4.Ng5", "This is the Hector Gambit");
add("1.e4 c6 2.d4 Nf6", "This is the Caro-Masi Defense");
add("1.e4 c6 2.d4 Na6 3.Nc3 Nc7", "This is the De Bruycker Defense");
add("1.e4 c6 2.d4 d5 3.f3", "This is the Maróczy");
add("1.e4 c6 2.d4 d5 3.Be3 dxe4", "This is the Mieses");
add("1.e4 c6 2.d4 d5 3.e5", "This is the Advance Variation");
add("1.e4 c6 2.d4 d5 3.e5 Bf5 4.g4", "This is the Bayonet Attack");
add("1.e4 c6 2.d4 d5 3.e5 Bf5 4.Nf3", "This is the Short Variation");
add("1.e4 c6 2.d4 d5 3.e5 Bf5 4.Nc3", "This is the an der Wiel Attack");
add("1.e4 c6 2.d4 d5 3.Nd2", "This is the Modern Variation");

add("1.e4 c6 2.d4 d5 3.Nd2 Qb6", "This is the Edinburgh Variation");
add("1.e4 c6 2.d4 d5 3.Nf3 dxe4 4.Ng5", "This is the Ulysses");
add("1.e4 c6 2.d4 d5 3.exd5", "This is the Exchange Variation");
add("1.e4 c6 2.d4 d5 3.exd5 cxd5 4.Bd3 Nc6 5.c3 Nf6 6.Bf4", "This is the Variation");
add("1.e4 c6 2.d4 d5 3.exd5 cxd5 4.c4", "This is the Panov-Botvinnik Attack");
add("1.e4 c6 2.d4 d5 3.exd5 cxd5 4.c4 Nf6 5.c5", "This is the Panov-Botvinnik Attack, Gunderam Attack");
add("1.e4 c6 2.d4 d5 3.exd5 cxd5 4.c4 Nf6 5.Nc3 Nc6 6.Bg5", "This is the Panov-Botvinnik Attack");
add("1.e4 c6 2.d4 d5 3.exd5 cxd5 4.c4 Nf6 5.Nc3 Nc6 6.Bg5 Qb6", "This is the Panov-Botvinnik Attack, Reifir (Spielmann) Variation");
add("1.e4 c6 2.d4 d5 3.exd5 cxd5 4.c4 Nf6 5.Nc3 Nc6 6.Bg5 dxc4 7.d5 Na5", "This is the Panov-Botvinnik Attack, Herzog Defense");
add("1.e4 c6 2.d4 d5 3.exd5 cxd5 4.c4 Nf6 5.Nc3 Nc6 6.Bg5 Qa5", "This is the Panov-Botvinnik Attack, Czerniak Variation");
add("1.e4 c6 2.d4 d5 3.exd5 cxd5 4.c4 Nf6 5.Nc3 e6 6.Bg5 Nc6", "This is the Panov, Carlsbad Line");
add("1.e4 c6 2.d4 d5 3.exd5 cxd5 4.c4 Nf6 5.Nc3 g6", "This is the Panov, Fianchetto Defense");

add("1.e4 c6 2.d4 d5 3.Nc3 g6", "This is the Gurgenidze System");
add("1.e4 c6 2.d4 d5 3.Nc3 g6 4.e5 Bg7 5.f4 h5", "This is the Gurgenidze System");
add("1.e4 c6 2.d4 d5 3.Nc3 b5", "This is the Gurgenidze Counter-Attack");
add("1.e4 c6 2.d4 d5 3.Nc3 Nf6", "This is the Campomanes");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.f3", "This is the Rasa-Studier Gambit");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Bc4", "This is the von Hennig");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Nf6", "This is the Knight");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Nf6 5.Bd3", "This is the Alekhine Gambit");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Nf6 5.Nxf6+ exf6", "This is the Tartakower Variation");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Nf6 5.Nxf6+ exf6 6.Bc4", "This is the Forgacs Variation");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Nf6 5.Nxf6+ gxf6", "This is the Nimzovich variation");

add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Nd7", "This is the Steinitz Variation");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Bf5", "This is the Classical Variation");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Bf5 5.Ng3 Bg6 6.f4", "This is the Maroczy Attack");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Bf5 5.Ng3 Bg6 6.Nh3", "This is the Flohr Variation");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Bf5 5.Ng3 Bg6 6.h4 h6 7.Nf3", "This is the Classical");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Bf5 5.Ng3 Bg6 6.h4 h6 7.Nf3 Nd7 8.h5", "This is the Spassky Variation");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Bf5 5.Ng3 Bg6 6.h4 h6 7.Nf3 Nd7 8.h5 Bh7 9.Bd3 Bxd3 10.Qxd3 e6 11.Bd2 Ngf6 12.O-O-O Be7", "This is the Lobron System");
add("1.e4 c6 2.d4 d5 3.Nc3 dxe4 4.Nxe4 Bf5 5.Ng3 Bg6 6.h4 h6 7.Nf3 Nd7 8.h5 Bh7 9.Bd3 Bxd3 10.Qxd3 e6 11.Bd2 Ngf6 12.O-O-O Bd6", "This is the Seirawan Variation");

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);
console.log(root);

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();