A long long time ago I asked this question which has since garnered about one thousand views. Now for something new.The demo for what I have is located here. I am generally happy with what I have built (although I did not implement all of the visuals (yet) of the original).I want to know if there is some improvements to my hitting algorithm that can be made, and maybe some suggestions of how to implement other fielders (aside from the pitcher that is already written).const logger = ((logArea, logDelay = 0) => {    const playArea = document.getElementById(logArea);    return {        delay: logDelay,        log(...args) {            let div = document.createElement('div');      // Can see the result in the console      console.log(...args);            setTimeout(() => {                // append to the top of the log                playArea.insertBefore(div, playArea.firstChild);                div.textContent = args.join(', ')            }, this.delay);            this.delay += logDelay;        },        logPad(...args) {            this.log();            this.log(...args);        }    };})('playArea', 120);const Team = (() => {    const numberOfPlayers = 9;    return class Team {        constructor(name) {            this.name = name;            this.players = [];            this.listeners = [];            this.playerIndex = 0;            this.score = 0;            this.generatePlayers();        }        toString() {            return this.name;        }        gainRun() {            this.score++;            logger.log(`${this} now have ${this.score} runs`);        }        generatePlayers() {            for (let i = 0; i < numberOfPlayers; ++i) {                this.players.push(new Player(`${this} Player ${i + 1}`, this));            }            this.pitcher = new Pitcher(`${this} Pitcher`, this);        }        nextPlayer() {            this.playerIndex %= numberOfPlayers;            let nextPlayer = this.players[this.playerIndex++];            logger.log(`${nextPlayer} is up to bat`);            return nextPlayer;        }        pitchTo(otherTeam) {            let outs = 0;            while (outs < 3) {                let nextPlayer = otherTeam.nextPlayer();                let strikes = 0;                let balls = 0;                while(strikes < 3 && balls < 4) {                    let pitch = this.pitcher.pitch();                    let hit = nextPlayer.acceptPitch(strikes, balls);                    if (hit.out) {                        outs++;                        logger.log(`${nextPlayer} hit the ball!`);                        logger.log(`${nextPlayer} hit a ${hit.name}`);                        break;                    }                    if (hit.bases === 0) {                        if (hit.strike) {                            strikes++;                            logger.log(`${nextPlayer} swung and missed`);                        } else {                            if (pitch.strike) {                                strikes++;                            } else {                                balls++;                            }                            logger.log(`${nextPlayer} waited and got a ${pitch.name}`);                        }                    } else {                        logger.log(`${nextPlayer} hit the ball!`);                        this.trigger('Hit', nextPlayer, hit);                        logger.log(`${nextPlayer} got a ${hit.name}`);                        break;                    }                }                if (strikes >= 3) {                    outs++;                    logger.log(`${nextPlayer} struck out`);                } else if (balls >=4) {                    logger.log(`${nextPlayer} walked`);                    this.trigger('Hit', nextPlayer, {                        bases: 1,                    });                }            }        }        addListener(object) {            if (!!object.trigger) {                this.listeners.push(object);            }        }        trigger() {            this.listeners.forEach((listener) => listener.trigger(...arguments))        }    };})();class Player {    constructor(name, team) {        this.name = name;        this.team = team;    }    toString() {        return this.name;    }    acceptPitch(strikes = 0, balls = 0) {        const random = Math.floor(Math.random() * 1000);        const swingRate = 500 - (75 * strikes) + (30 * balls);        if (random < swingRate) {            return {                name: 'swing and a miss',                bases: 0,                out: false,                strike: true,            };        } else if (random < 880) { // wait for the pitch or hit into an out            if (random > 830) {                return {                    name: 'fly out',                    bases: 0,                    out: true,                };            } else if (random > 810) {                return {                    name: 'ground out',                    bases: 0,                    out: true,                };            }            return {                name: 'wait',                bases: 0,                out: false,            };        } else if (random <= 1000) { // hit the ball            if (random > 940 && random < 970) {                return {                    name: 'double',                    bases: 2,                    out: false,                };            }            else if (random >= 970 && random < 995) {                return {                    name: 'triple',                    bases: 3,                    out: false,                };            }            else if(random >= 995){                return {                    name: 'homerun',                    bases: 4,                    out: false,                };            } else {                return {                    name: 'single',                    bases: 1,                    out: false,                };            }        }    }};class Pitcher extends Player {    static get pitchTypes() {        // @todo math this SH*T up        return [{            name: 'strike',            strike: true,        },{            name: 'ball',            strike: false,        },{            name: 'strike',            strike: true,        },];    }    pitch() {        const types = Pitcher.pitchTypes;        const random = Math.floor(Math.random() * (types).length);        logger.log(`${this} threw the ball`);        return types[random];    }};const Stadium = ((diamondArea) => {    const diamondDiv = document.getElementById(diamondArea);    let firstBase = false,        secondBase = false,        thirdBase = false,        home = false;    return class Stadium {        constructor(a, b) {            // @todo checks that a and b are team objects            this.teams = [a, b];        }        firstBase(player) {            if (firstBase && player) {                logger.log(`${firstBase} ran to second`);                this.secondBase(firstBase);            }            firstBase = player;        }        secondBase(player) {            if (secondBase && player) {                logger.log(`${secondBase} ran to third`);                this.thirdBase(secondBase);            }            secondBase = player;        }        thirdBase(player) {            if (thirdBase && player) {                logger.log(`${thirdBase} ran home`);                this.home(thirdBase);            }            thirdBase = player;        }        home(player) {            player.team.gainRun();        }        single(player) {            this.firstBase(player);            logger.log(`${player} ran to first base`);        }        double(player) {            this.single(player);            this.secondBase(player);            this.firstBase(false);            logger.log(`${player} ran to second base`);        }        triple(player) {            this.double(player);            this.thirdBase(player);            this.secondBase(false);            logger.log(`${player} ran to third base`);        }        homerun(player) {            this.triple(player);            this.home(player);            this.thirdBase(false);            logger.log(`${player} ran to home base`);        }        get baseTypes() {            return [this.homerun, this.single, this.double, this.triple];        }        clear() {            firstBase = secondBase = thirdBase = false;            return this;        }        playGame(innings) {            let extraInnings = 0;            let [teamA, teamB] = this.teams;            logger.log(`${innings} inning game!`);            this.listenToTeamActions();            for (let i = 1; i <= innings; ++i) {                logger.logPad(`Playing inning ${i} of ${innings}`);                this.playInning(i);            }            while (teamA.score === teamB.score) {                extraInnings++;                logger.logPad(`EXTRA INNINGS!`);                logger.logPad(`Score: ${teamA}: ${teamA.score} to ${teamB}: ${teamB.score}`);                this.playInning(`EXTRA ${extraInnings}`);            }            logger.logPad(`GAME OVER${extraInnings ? ` WITH ${extraInnings} EXTRA INNINGS!` : '!'}`);            logger.logPad(`Score: ${teamA}: ${teamA.score} to ${teamB}: ${teamB.score}`);        }        playInning(index) {            let [teamA, teamB] = this.teams;            logger.logPad(`${teamB} are up to bat in inning ${index}`);            teamA.pitchTo(teamB);            this.clear();            logger.logPad(`${teamA} are up to bat in inning ${index}`);            teamB.pitchTo(teamA);            this.clear();        }        listenToTeamActions() {            let [teamA, teamB] = this.teams;            teamA.addListener(this);            teamB.addListener(this);        }        trigger(event, ...args) {            // @todo make first letter of event uppercase            if (!!this[`on${event}`]) {                this[`on${event}`](...args);            }        }        onHit(player, type) {            const baseTypes = this.baseTypes;            const numberOfBases = type.bases % 4;            baseTypes[numberOfBases].bind(this)(player);        }    };})('diamond');const mets = new Team('Mets');const yankees = new Team('Yankees');const citiField = new Stadium(mets, yankees);console.log(mets, yankees);citiField.playGame(9);
Self-playing Baseball game -- An es6 update
javascript;game;ecmascript 6