JSFiddle - React, Tailwind, and code Playground

by rgthree

HTML

<fieldset>
  <legend>Test Data</legend>
  <form id="test-form" name="test-form">
    <label>Num of Prompts:</label>
    <input name="prompts" value="17" type="text" required pattern="\d+"><br>

    <label>Num of Choices (per prompt):</label>
    <input name="choices" value="4" type="text" required pattern="\d+"><br>

    <label>Num of Entries:</label>
    <input name="entries" value="2" type="text" required pattern="\d+"><br>

    <label>Automate Number of Tests:</label>
    <input name="tests" value="1000" type="text" required pattern="\d+"><br>

    <input type="submit" value="Run Test" />
  </form>
</fieldset>
<br><br><br>
<fieldset>
  <legend>Results</legend>
  <pre id="results"></pre>
  <div id="graph"></div>
  <div id="curve"></div>
</fieldset>

CSS

html {
  font-size: 10px;
  box-sizing: border-box;
}
*, *::before, *::after {
  box-sizing: inherit;
}
body {
  background: #fff;
  font-family: Arial, sans-serif;
}
fieldset {
  border: 1px solid #ccc;
}
legend {
  background: #ccc;
  font-size: 1.1rem;
  padding: 0.5rem 1rem;
}
label {
  display: inline-block;
  width: 200px;
  font-size: 1.3rem;
}
label + input[type="text"]  {
  display: inline-block;
  width: 75px;
  font-size: 1.3rem;
}
label + input[readonly]  {
  width: 175px;
}

pre#results {
  white-space: pre-line;
  font-size: 1.4rem;
}

#graph,
#curve {
  font-size: 0;
  display: block;
  width: 100%;
  margin: 1rem auto;
  border: 1px solid #ccc;
  padding: 1rem;
}
#graph .keys,
#graph .bars {
  width: 100%;
  display: flex;
  flex-direction: row;
  align-items: flex-end;
}

#graph .bars {
  height: 100px;
  border-bottom: 1px solid #aaa;
}
#graph .bar-container,
#graph .key {
  display: inline-block;
  width: 33px;
  overflow: hidden;
  vertical-align: bottom;
  border: 1px solid #fff;
  text-align: center;
}

#graph .bar-container {
  height: 100%;
  display: inline-flex;
  flex-direction: column;
  justify-content: flex-end;
  opacity: 0.75;
  transition: all 0.25s ease-in-out;
  flex-grow: 1;
  flex-shrink: 1;
}
#graph .bar-container:hover {
  opacity: 1;
}
#graph .bar-container > * {
  flex-grow: 0;
  flex-shrink: 0;
}
#graph .bar {
  max-height: 0%;
  background: rgba(145, 0, 0, 0.55);
  color: #fff;
}
#graph .bar-container label {
  display: block;
  font-size: 1rem;
  background: #fff;
  color: #000;
  width: 100%;
}

#graph .-highest .bar {
  background: rgba(145, 0, 0, 1);
}

#graph .-zero .bar {
  background: #aaa;
}
#graph .-zero label {
  opacity: 0.75;
}
#graph .key {
  font-size: 1rem;
  flex-grow: 1;
  flex-shrink: 1;
}
#graph.-on .bar {
  transition: max-height 0.5s ease-in-out;
  max-height: 100%;
}
#graph .bar-container label {
  color: #fff;
}
#graph.-on .bar-container label {
  transition: color 0.5s ease-in-out;
  color:...

JavaScript

'use strict';

let alphabet = 'abcdefghijklmnopqrstuvwxyz'.toUpperCase().split('');

class Util {
  static $(id) {
    return document.getElementById(id);
  }

  static random(min = 0, max = 100) {
    return Math.random() * (max - min) + min;
  }

  static arrayMean(arr) {
    var sum = arr.reduce((a, b) => a + b);
    return sum / arr.length;
  }

  static arrayMedian(arr) {
    var medianIndex = (arr.length - 1) / 2;
    var median;
    if (medianIndex === Math.floor(medianIndex)) {
      median = arr[medianIndex];
    } else {
      median = (arr[Math.floor(medianIndex)] + arr[Math.ceil(medianIndex)]) / 2;
    }
    return median;
  }
  
  static arrayVariance(arr) {
    let mean = Util.arrayMean(arr);
    let squared = arr.reduce((a, b) => a + Math.pow(b - mean, 2));
    return squared / (arr.length - 1); // Divide by n-1, instead of n
  }

  static roundDecimal(n) {
    return Math.round(n * 1000) / 1000;
  }

  static percentage(num, total) {
    return Util.roundDecimal((num / total) * 100);
  }

  static chooseRandom(choices) {
    var distribution = 100;
    return choices[Math.floor(Util.random(0, distribution - 1) / (distribution / choices.length))];
  }
}


/**
     * A entry of answers.
     */
class Entry {
  constructor(choices) {
    this.choices = choices.slice(0);
    this.chosen = new Map();
    this.choices.forEach(c => this.chosen.set(c, 0));
  }

  choose() {
    let choice = Util.chooseRandom(this.choices);
    this.chosen.set(choice, this.chosen.get(choice) + 1);
    return choice;
  }
}


/**
     * A full test run.
     * @class
     */
class Test {
  constructor(numOfPrompts, numOfEntries, numOfChoices) {
    this.numOfPrompts = numOfPrompts;
    this.numOfEntries = numOfEntries;
    let choices = alphabet.slice(0, numOfChoices);

    let entries = [];
    do {
      entries.push(new Entry(choices))
    } while (--numOfEntries);

    this.promptsAnsweredTheSame = 0;
    do {
      let promptAnswers = [];
      entries.forEach(e =>...