2018.03.18 16:00 | Kurt Tomlinson
I recently created an online version of Connect Four as part of a coding challenge. It is a SPA with the frontend written in Vue.js and the backend in Ruby on Rails 5.1. The code is available on Github in a pair of repositories. It's currently hosted on Heroku, so you can play it now.
If you're interested, this is what the user interface looks like:
What can I say? I'm a coder, not a designer.
To start, I made a game of connect four that runs in the console. That is the code that's in the first repository, console-connect-four. The most interesting part of the code is the AI that drives the computer player.
The game state in a game of connect four can be visualized as a sequence of past moves and a tree of future possible moves. When it is a computer player's turn to move, it has about 7 moves to choose from (fewer if any of the columns on the game board are filled up). After that, the computer player's opponent has about 7 moves to choose from as well. By traversing every node in the tree of future moves, we can find which sequences of moves result in a win, loss, or tie.
This is where the Minimax algorithm comes into play. The Minimax algorithm assumes that each player is going to choose whatever move is best for him/her. Using that idea as a astarting point, we can assign point values to each possible future move. If a future move results in a win, then it gets a good score, say +1. If a future move results in a loss, then it gets a bad score, say -1.
So then what score do we assign to a move that doesn't result in an immediate win or loss? How can we score a move that isn't a game-ending move? Well, if that move is a move by the current player, then we assign it the minimum score of all of the moves immediately following that move. Why? Because the following moves will be made by the opponent, and we assumed that the opponent will make whatever move will be most likely to let him/her win.
Similarly, if the current move we're analyzing is a move by the opponent, then that move gets the maximum score out of the scores from all of the immediately following moves. This is true for the same reason. At each step, we assume that the player making the move is going to pick the best move for him/herself.
The second repository, connect-four-rails-vue, is basically just a graphical and web wrapper around the console game. It imports the console code as a git submodule and leverages it heavily. I'd like to turn the console game code into a gem so it would be a little cleaner to import.
The Rails portion of the code is just a super simple controller and a model that wraps the console game code.
The Vue portion is a bit more interesting. It presents the user interface and gets inputs from the user. It uses Axios to make GET requests to the server to figure out if a move is game-ending or not (and what the result is if the game is over), and it also asks the game server where the computer players should move. It does this by passing the sequence of previous moves to the server so the server can know the current state of the game. In this way, there's no need to store any state on the server.
Well, the choice of Ruby on Rails was easy because it's the framework I'm most familiar with. I'd like to learn Elixir and Phoenix, but that will have to wait. I'm looking forward to functional programming and a framework that excels at handling high-concurrency workloads.
Choosing Vue.js was a bit more difficult. The current king of the frontend wars is React. I think it's pretty clear at this point that Angular is fighting a losing battle, and the other frameworks from yesteryear, Ember, Backbone, etc., are all running out of steam as well.
Vue on the other hand seems to be catching up to React. It's getting starred on Github more quickly than React. But that's not everything. React has a more mature community with some really great tooling.
React also has React Native, a way to create native apps using the same codebase as your web frontend. It's well supported and actively use by tons of people. Vue is a little bit less mature on this front, but there's still hope: Weex and NativeScript are two ways that Vue code can be reused for iOS and Android, but they both have their own individual drawbacks that I won't cover here.
In the end, I choose Vue because I hadn't used it before, and I thought this toy project would be a good place to learn it. I'm really happy with Vue's single-file components because they make file organization for a project so much easier. I also found the learnign curve for Vue to be less steep than React's, but that might be because I had already chipped away from of it by learning React first. React and Vue have some similarities that probably made it much easier for me to learn Vue than if I had just tried to learn Vue first.
Photo by Park Troopers