A few weekends ago I made this little project to brush up on my Python skills. Sometimes it’s hard to go back and forth between C at work and Python at home since C is very verbose and Python seems to do everything for me, but that only adds to the fun 🙂
This project solves the 2048 game in a browser window by reading in pixel values and searching the tree of possible boards. It consists of 3 main features: reading the board, the search tree, and evaluation.
Reading the Board
I had several options for reading the board, but I chose what I felt to be the simplest method. Using the Python Imaging Library my bot takes a screenshot of the game area in a pixel range that I measured by hand. Since each square value has a different color, its read one pixel from each of the 16 spaces and uses the RGB values to find the corresponding number. This gives all 16 square values, which is the only information necessary to determine the correct move.
This bot uses a simple search tree compared to my chess ai, although it still has its own complexities. It creates a search tree by searching all player and computer moves to a total depth of 5 moves. The computer “player” tries to minimize the score, and the bot tries to maximize it. By doing that, the algorithm finds the worst-case scenario combination of computer moves for each branch and picks the move that will minimize the damage to the overall score.
I added a penalty for having the largest number anywhere but the top left, and this penalty is greater at shallower search depths. This encourages the bot to keep the highest square in the top left, and if it has to break that rule it will wait as long as possible. Once the best move is found, the bot moves the board using a simulated arrow key press via the win32api.
The most important part of evaluation is rewarding combinations. To accomplish this, I assigned a score to each square value, and the sum of these scores is the majority of the final evaluation. To encourage combinations, the value of a square is always more than double the score of the next highest square. Therefore, it is always beneficial to combine.
To encourage keeping the highest squares on the top row, each square’s score is multiplied by a bonus depending on its place on the board. Higher spaces have higher bonuses.
Here’s a video of the bot beating the game. It’s certainly not the most exciting video, but it shows the general strategy that I programmed. The free screen recorder gave less than perfect quality, unfortunately, and the crash at the end is due to unrecognized RGB values when the congratulations screen pops up.
Overall, it could be improved with some more work, but I didn’t want to get caught up in another massive AI project like my chess engine. I wanted a quick brainteaser, and this was certainly a satisfying project to keep me busy over a weekend.