KISS: Keep It Simple, Smarty!

Smart people keep things simple.

August 19, 2021

The title is a more optimistic variation on the KISS principle, also related to YAGNI. Faithful readers of this blog may recall a recent posting wherein I clearly failed to heed these two principles. I should have gone for the most straightforward solution, being willing to rework later only if needed.

I spent most of today battling a minimax algorithm for a tic-tac-toe AI. I ended up questioning whether the logic that determined the winner of a grid was working correctly, so I ripped out all of the fancy grid-scanning logic I put in last time and replaced it with the following very simple code:

(defn winner? [grid mark]
  (or (and (= (grid 0) mark) (= (grid 1) mark) (= (grid 2) mark)) ; row 1
      (and (= (grid 3) mark) (= (grid 4) mark) (= (grid 5) mark)) ; row 2
      (and (= (grid 6) mark) (= (grid 7) mark) (= (grid 8) mark)) ; row 3

      (and (= (grid 0) mark) (= (grid 3) mark) (= (grid 6) mark)) ; col 1
      (and (= (grid 1) mark) (= (grid 4) mark) (= (grid 7) mark)) ; col 2
      (and (= (grid 2) mark) (= (grid 5) mark) (= (grid 8) mark)) ; col 3

      (and (= (grid 0) mark) (= (grid 4) mark) (= (grid 8) mark))   ; diag 1
      (and (= (grid 2) mark) (= (grid 4) mark) (= (grid 6) mark)))) ; diag 2

(defn winner [grid]
  (cond (winner? grid X) X
        (winner? grid O) O
        :else nil))

This code is not flashy. It almost screams, "REWORK ME!" (and I will), but it works, and it also runs much faster than the previous grid-scanning implementation, which made certain testing scanarios more feasable.

Depending on what features are needed in the future, it's likely that this code will get a rework, but maybe not...