Connect 4

Who doesn't enjoy a good board game? This is the classic board game, Connect 4. Touching any of the below headers collapse menu will drop-down relevant information about the game. If you want to read more about the game and AI implementation, click here to read my blog article on the subject. Thanks for playing. Have fun! 😀

The game is played on a 7-column-wide by 6-row-tall vertical board. One player is 🔴 red and the other is 🔵 blue. The 🔴 red player plays first. Players take turns placing chips into columns on the board. When a colored chip is placed into a column, it falls to the bottom available space in that column. The first player to get 4 chips in a row (horizontally, vertically, or diagonally) is the winner.

Both the 🔴 red player and the 🔵 blue player can be set to 🧐 "human" or 🤖 "computer/AI" (we'll call such players "computer" or "AI" interchangeably). Thus the game can be played with 🧐human-vs-🧐human, 🧐human-vs-🤖computer, or 🤖computer-vs-🤖computer. To adjust if a player is a "human" or a "computer", flip the "Is Computer" switch under each player's colored avatar.

If a player is set to 🧐 "human", that player can take their turn by clicking on the column they wish to drop their chip into. By default, if a player is set to 🤖 "computer", the AI will automatically take that player's turn. After each turn, the game will check for a winner or a tie (no more moves left). If there is a winner or a tie, the game will end, the winner will be displayed along with their winning combination, and each player's win-loss-draw score will be updated. At this point (or any point during the game) the "Reset Board" button can be clicked to reset the board to the starting state, ready for a new game. The "Reset Scores" button will reset the win-loss-draw scores for both players.

Hopefully, all of the settings are intuitive and straightforward. Just in case, here's a description of what each one does.

Is Computer

Sets whether the above-listed player is a 🤖 "computer/AI" or a 🧐 "human" player. If flipped on, the 🤖 AI will play for that player and a 🧐 human cannot make moves for the player.

AI depth slider

Sets the depth of the 🤖 AI's search tree. The higher the depth, the more likely the 🤖 AI will make a good move. The lower the depth, the more likely the 🤖 AI will make a bad move. The default is set to 4.

Auto-move

If an 🤖 AI player's "auto-move" switch is flipped "on", the 🤖 AI player will automatically make a move when it is their turn. If the switch is flipped "off", the 🤖 AI player will not make a move automatically. Instead, you can touch that player's "move" button to have them make a move.

Auto-reset until games played

By default, when a game ends, the board will stay in the end-game state until the "Reset Board" button is touched. If the "Auto-reset until games played =" switch is flipped on, the board will automatically reset after the game ends.

This is useful for 🤖AI-vs-🤖AI matches where you want to see the end win-loss-draw ratio for each 🤖 AI player after a certain number of back-to-back games are played. This setting is paired with a number field. When the total number of games played (wins + losses + draws) reaches that number, the board will cease to auto-reset. Setting this number-field to "0" will make the games auto-reset indefinitely.

Show AI move weights

The 🤖 AI's moves are decided by looking into the future for a certain number of moves (set by the above-mentioned "AI depth slider"). See the below section "The implementation" for details on the 🤖 AI's implementation. Ultimately, this results in weighted values for each column, and the 🤖 AI will choose the highest (most positive) weighted column (choosing randomly if there are multiple equally-highest rated columns). If the "Show AI move weights" switch is flipped on, these weighted column values appear below the game board.

Show move animations

If this switch is flipped "on", each chip placed in a column will show an animation of the chip falling to the bottom available space. Otherwise, the chip will simply appear at the bottom available space, sans falling.

I wrote a whole blog article on the game implementation and how I built the AI through 5 layers of iteration and testing. You can find that blog article here. But it's pretty involved, so I'll summarize a little bit about the game and the final AI version AI version here.

Language choice

I wrote connect 4 in JavaScript so it runs entirely client-side (ie once the page loads, you could continue playing without an internet connection). The JavaScript code utilizes some of the latest ES6 features, and it is not transpiled to an older version of JavaScript. Thus it might not behave correctly on older browsers. But that's not my audience here, so I'll just leave it as is. It behaves well on the big 3 browsers (Chrome, Firefox, and Edge) and that's good enough for me.

My most comfortable language is Python, so the JavaScript implementation was more of a challenge, but ultimately it results in a better user experience if server-side interactions are not required. If you're interested in seeing the source code for the game, I'll link to the JavaScript "here", the CSS "here", and the HTML you can get (on a computer) by right-clicking anywhere on the page and selecting "inspect".

The AI

The AI looks at future board states for potential wins for either player and tries to pick moves that will result in wins for itself and will avoid wins by its opponent. The AI plays out theoretical moves for itself and its opponent a certain number of moves into the future (set by its depth setting). First, it will put a chip for its color in all available columns (potentially 7 columns), and it will remember the resulting board states. For round 2, it will put a chip for its opponent in each column from each board state of round 1, alternating turns every round. This continues until the number of rounds equals the AI's depth setting. At depth 0, the AI picks a random available move without evaluating win chances.

Each round, after placing all available chips, the AI sees which future board states resulted in wins for itself or wins for its opponent. Potential future wins add positive weighted values toward a column, while potential future losses (or missed wins) add negative weighted values toward a column. At a given future board state, after adding a weighted value to a winning column or subtracting a weighted value from a losing column, that board state will be discarded for calculations of future rounds (ie the game has already ended at that point so there are no future boards from that state). The AI detects "missed wins" by detecting if a chip placement in round 1, followed by an opponent chip placement in the same column in round 2 will block a win for the AI in round 3. Such "missed win" scenarios are treated like losses and result in negative weighted values toward the column.

Lower numbered rounds (rounds with board states nearest the current board state) result in quicker wins/losses, and thus they are weighted higher than higher-numbered rounds (rounds with board states in the more distant future). One can see how the time and memory for round calculations will increase exponentially with an increasing depth setting. In round 1 there are only 7 potential board states to consider. In round 2, there are 7 x 7 = 49 potential board states. By round 6, there will be 7 x 7 x 7 x 7 x 7 x7 = 117,649 potential board states to calculate. Thus AI calculations at depths 0-4 seem near-instant, calculations at depth 5 have a half-second lag, calculations at depth 6 take a couple of seconds, and soon there-after the system hangs from taking too long to calculate. For more details on the game and the AI's implementation, see the blog article linked above.


             

Red player
(human)

Red Player

Red Score:
(W-L-D)
0-0-0

Blue player
(human)

Blue Player

Blue Score:
(W-L-D)
0-0-0