Search code examples
javascripttreeswitch-statement

More efficient way to implement a CYOA game instead of using switch


I'm trying to implement a function that will return a result (blue text) based on the combination of choices according to this tree diagram:

tree diagram of choices

I currently have three functions:

  • optionA click - handles the left option click using switch
  • optionB click - handles the right option click using switch
  • reset click - resets everything to initial state

This approach works fine but I was wondering if there is a more effective way of achieving the same result. My current code is not really scalable. It's repetitive and I think it could potentially be shortened but I'm not really sure how to approach it.

let optA = document.querySelector(".optionA");
let optB = document.querySelector(".optionB");
let result = document.querySelector(".result");
let reset = document.querySelector(".reset")

function handleOptAClick() {
  if (result.innerHTML === '') {
    switch (optA.innerHTML) {
      case 'Hot':
        optA.innerHTML = "Leaf";
        optB.innerHTML = "Flower";
        break;
      case 'Leaf':
        optA.innerHTML = "Pure";
        optB.innerHTML = "Blend";
        break;
      case 'Alcohol':
        optA.innerHTML = "Plum";
        optB.innerHTML = "Rice";
        break;
      case 'Pure':
        optA.innerHTML = "Sunlight";
        optB.innerHTML = "Shade";
        break;
      case 'Plum':
        result.innerHTML = "Umeshu";
        break;
      case 'Bacteria':
        optA.innerHTML = "Yeast";
        optB.innerHTML = "Milk";
        break;
      case 'Sunlight':
        result.innerHTML = "Sencha";
        break;
      case 'Yeast':
        result.innerHTML = "Kombucha"
        break;
      default: 
        console.log("Something went wrong");
    }
  }
}

function handleOptBClick() {
  if (result.innerHTML === '') {
     switch (optB.innerHTML) {
    case 'Cold':
      optA.innerHTML = "Alcohol";
      optB.innerHTML = "Soft";
      break;
    case 'Flower':
      result.innerHTML = "Sakura"
      break;
    case 'Soft':
      optA.innerHTML = "Bacteria";
      optB.innerHTML = "Soy";
      break;
    case 'Blend':
      result.innerHTML = "Genmaicha"
      break;
    case 'Rice':
      result.innerHTML = "Sake"
      break;
    case 'Soy':
      result.innerHTML = "Soymilk"
      break;
    case 'Plum':
      result.innerHTML = "Umeshu";
      break;
    case 'Shade':
      result.innerHTML = "Matcha";
      break;
    case 'Milk':
      result.innerHTML = "Yakuruto";
      break;
    default: 
      console.log("Something went wrong");
     }
  }
}

function handleResetClick() {
  optA.innerHTML = "Hot";
  optB.innerHTML = "Cold";
  result.innerHTML = '';
}
<div>
  <button class="optionA" onClick=handleOptAClick()>Hot</button>
  <button class="optionB" onClick=handleOptBClick()>Cold</button>
  <button class="reset" onClick=handleResetClick()>Reset</button>
  <p class="result"></p>
</div>

I'm thankful for any suggestions.


Solution

  • There is quite some repetition in your code. You could improve it by making a data structure to represent the tree, instead of hard-coding every parent-child relation in the code.

    For instance, a tree node could be an array with three elements: the choice (to be displayed), the (nested) tree node for the left choice, and the (nested) tree node for the right choice. A leaf in the tree would be an array with only two entries: the choice (to be displayed) and the corresponding result to be displayed.

    The code could then focus on maintaining a reference to what is the current node in the tree, i.e. the node that corresponds with the choices that were made up to this point.

    I would also suggest to not define the click handlers with HTML attributes, but with code (using addEventListener).

    Here is how that would look:

    const tree = 
    ["Root",
        ["Hot",
            ["Leaf",
                ["Pure",
                    ["Sunlight", "Sencha"],
                    ["Shade", "Matcha"]
                ],
                ["Blend", "Genmaicha"]
            ],
            ["Flower", "Sakura"]
        ],
        ["Cold",
            ["Alcohol",
                ["Plum", "Umeshu"],
                ["Rice", "Sake"]
            ],
            ["Soft",
                ["Bacteria",
                    ["Yeast", "Kombucha"],
                    ["Milk", "Yakuruto"]
                ],
                ["Soy", "Soymilk"]
            ]
        ]
    ];
    
    let current = tree;
    
    const [left, right] = document.querySelectorAll(".option");
    const result = document.querySelector(".result");
    const reset = document.querySelector(".reset")
    
    left.addEventListener("click", () => choose(current[1]));
    right.addEventListener("click", () => choose(current[2]));
    reset.addEventListener("click", () => choose(tree));
    
    function choose(choice) {
        if (choice.length == 2) { // We're at a leaf
            result.textContent = choice[1];
        } else {
            current = choice;
            left.textContent = current[1][0];
            right.textContent = current[2][0];
            result.textContent = "";
        }
    }
    <div>
      <button class="option">Hot</button>
      <button class="option">Cold</button>
      <button class="reset">Reset</button>
      <p class="result"></p>
    </div>