Planning to Win at Gin

The most important aspects of Gin Rummy are making melds, reducing the point value of your hand, and knocking as quickly as possible. A meld is a combination of cards with matching face values or a run of sequential cards in the same suit, both of which must include at least three cards. Any cards in a meld are worth zero points, which leads to the next important facet. You want to reduce the point value of your hand as much as possible in case another player ends the game first, as they will get the point value of your hand added to their total. You also need to be below a certain total point value to knock. Knocking is when a player ends the current round of a game after their total hand value has gone low enough, and they then see if they have the lowest value and how many points they get from other players. There are other complexities involved in Gin, but those are essentially the key points.

For the second assignment of my Artificial Opponents programming course, we have to make an AI to play Gin. Some of the added complexities of the game are picking cards to discard, watching if the opponent takes the discarded card, watching what they discard, and monitoring what cards are actually still available in the game. The great thing about programming an AI to play this game is computers are exceptionally good at tracking all of this data and much better than the average human. You can know exactly which cards are still left in the deck based on your hand, the discard, what the other player has taken from the discard, and the combination of cards in the deck. This is how you form a strategy to play the game and ultimately win as often as possible.

I want to keep my Gin playing AI fairly simple, but I want to assure that it wins as much as possible. One of the key points will of course be what you could call counting cards, just tracking probabilities of getting any given card that is needed. Using that as the basis, I will use a technique suggested by the professor and give each card in hand a utility or usefulness value. This value will be based on a number of factors, including what I spoke about as the priorities of the game. Those are if the card adds to a meld or is close to doing so, if it is high value in the hand and not in use, and if getting rid of it or keeping it will get you closer to ending the round by knocking. Beyond that I will include factors such as if the other player seems to want cards of that type which would most likely be to create melds, and based on if getting another matching card to create a meld is still possible given remaining cards. Combining all these factors with a weighting algorithm will give a high chance of making the best choices with cards.

The keys to this strategy in terms of code will rely primarily on a class with functions to track all available cards in the deck, essentially counting the cards. It will also need a series of functions which assess each card based on the criteria given above to determine utility. It will then need to store those utility values for each card in the overarching card tracking class. Once the utility of all cards is determined, there will be a function to pick which card should be discarded, most likely based on its utility value, and then determine if it should try to knock or if the hand has Gin for example, meaning a zero point value. While I am not an expert at Gin, I have also found some strategic references listed below which can help me determine how to weight my algorithm. While the above laid out strategy does not assure optimal Gin playing, it should come close and also allow constant tweaking for the best results.

https://www.gamecolony.com/gin_rummy_game_strategy.shtml
http://dreamquestgames.com/gin-strategy-tips-10-golden-rules/
http://www.rubl.com/rules/gin-rummy-tips.html

Coding Minesweeps

To start with actually coding, I did try to research if there was an optimal starting position. I essentially found that while starting in a corner may yield a slightly higher chance of completing a game, it also may decrease the speed of winning a game which is easy to solve. So, I called it a wash and decided I would just start in the middle.

After picking a starting point, I moved on to processing cells revealed after the first click. Essentially, any cell which is completely empty can be ignored and does not need to be stored, but also does not need to be clicked on. Numbered cells should be stored to see if any cells around them are mines or can be safely revealed. After all the cells around a numbered cell are processed, it can be marked as a finished cell which does not need to be processed again. After all numbered cells have been processed, the solver can check through the board again for new numbered cells. However if no changes were made on the last round, a guess has to be made.

The difference with my solver is that as numbered cells have adjacent cells processed which are not marked as mines or safe and stored in a probability map. The probability map stores cell numbers and the number of adjacent numbered cells which indicate there may be a mine in that spot. If a guess must be made without confidence, the solver will pick the cell with the lowest probability of having a mine. This makes the guessing somewhat random, but at least attempts to avoid cells which have a high probability of being mined and also doesn’t leave the entire game up to pure chance.

I believe my solver will win slightly more games than average, but will overall be slower than average. Therefore it will be middle of the pack. Below those which used the most well known algorithms such as those from the Harvard paper, and above those which rely solely on speed or use much more time consuming algorithms.

Planning Minesweeps

The first assignment for my senior level programming class, Artificial Opponents, is to solve mine sweeper as quickly and accurately as possible. To determine how I should code my solver, I first did research on optimal solutions to Minesweeper. My searches quickly led to the discovery that Minesweeper as a mathematical or computational problem is NP-Complete, which makes an optimal solution quite complex.

NP-Complete is a term which means the problem is NP-Complex and also NP-Hard. NP stands for Non-deterministic Polynomial-time. Polynomial-time is a reference to the number of operations needed to complete and algorithm given the size of the problem. If it is non-deterministic, that means it can’t be exactly quantified because even given the same problem, the algorithm may behave differently each time. In other words, there is no one best solution. NP-Hard means it is part of the hardest set of NP problems. NP-Complete refers to the hardest set of NP problems because it is the combination of the above.

What this all means for coding a solver for Minesweeper is that there is no single simple solution. There are many steps to solving the game, there are many methods to reach the same end goals, and the outcomes are not assured to be the same each game played. So, the best one can hope for with a minesweeper solver is to get as much speed as possible with as few lost games as possible, but it is at least uncertain if it is possible to win every time and most likely impossible to get the fastest solution every game. Therefore the focus of my code will be on losing as infrequently as possible and making the code as efficient as it can be.

All that said, while I have researched various techniques to solve, I may attempt to solve it myself before using one of the commonly known solving methods. There are for example articles from Harvard and a blog called Lucky’s Notes which describe various techniques to win the game often and quickly. Before finding these articles though, I had thought of my own idea on how to solve the problem and I will describe it below.

Essentially, the simplest rule in Minesweeper is knowing that if you reveal a square, and the number of open areas around the square is equal to the number of adjacent mines revealed, every open square is a mine. After that, if a square has a number of known mines around it equal to the number of mines around the square, any other open squares are not mines. Given those two rules, a lot can be accomplished in a single game. However for more complex problems, there needs to be more deductive reasoning.

My thoughts on how to solve more unknown squares is essentially to assign them probability. If a square has a given number, such as 1, and there are 2 open squares around it, then give each of the two open squares a single probability mark. If another square near one of those squares has a number as well, that square will receive a second probability mark. After all the uncertain squares in an area are given probability marks, the solver will choose the square with the least marks, assuming it is not a mine.

I plan to use this technique and see if it is effective. If it is, I will try to improve and refine it. If not, I may switch to a more well known technique as described in one of the blog posts. This AI should scale with the larger games because the simple rules still apply, and I will limit the probability checking to given areas based on the number of already revealed squares. The area will most likely be limited to all currently open squares adjacent to revealed squares. Hopefully this will make solving quick and accurate.

For major classes in the actual code structure, there will be a structure to hold information on sets of squares. These sets will be already picked squares, squares know to hold mines, and a list of current squares marked for probability and what that probability is. The functions of this class will filter through these lists after picking which action to take next based on the success of the previous action. This includes picking the first square, marking any known mines, marking probability, and then making the next guess. Ideally this will be a fairly lightweight solution that can also be quick and accurate.

Sources:
http://web.mat.bham.ac.uk/R.W.Kaye/minesw/ordmsw.htm
https://en.wikipedia.org/wiki/NP-completeness
https://en.wikipedia.org/wiki/NP-hardness
https://en.wikipedia.org/wiki/NP_(complexity)

Click to access BECERRA-SENIORTHESIS-2015.pdf

https://luckytoilet.wordpress.com/2012/12/23/2125/