Solitaire Week 4: Getting Back in the Code
Most of my work on the project this week has been refamiliarizing myself with the code base, and relearning how I put stuff together late last year. Since I wanted to approach this task with some direction, and since I had filed a couple of defects for myself on the project, I figured now would be a good time to set about fixing those. The defects that I had filed centered on the game’s auto-completion functionality and how the game checks for a win-on-board state. Whenever any valid move is made, the GameMaster checks the entire board state to determine if the player has met the game’s win conditions, and if he hasn’t then the GameMaster will check if the player has met the “win-on-board” conditions. A win-on-board state means that, while the player may not have yet won the game (have 13 cards in all four of the card heaps), they have the game board configured in such a way where a win is guaranteed. If GameMaster detects such a state, it’ll go ahead and finish the game for them. I had previously defined the win-on-board conditions as such:
- All cards in all columns have been turned face up
- The deck has no cards in it
- The stack (where cards from the deck are moved to) only has one card in it
This made sense to me when I was first implementing the logic, as if those conditions are met then all cards are available to the user to go ahead and finish out the game, it’s just a matter of putting the cards in their heaps. However, I have since found that the only condition we really need to care about is whether or not the cards in the columns are face up. If all cards in all columns are face up, then you have already won the game (you just need to put all the cards in their respective heaps). In addition to this, I found that by checking for win-on-board conditions after a successful move, there was a chance that the move that met those conditions was a card move off of a column that flipped the last “face down” card face up, and unfortunately the check for this usually happened before the card had been flipped up after the move. So, basically, a move would occur that caused the last face down card in a column to become face up, but we were checking for the conditions after the move was made, but before the column finished turning the card face up, so the check would fail. The player would then have to make any other move (eg moving a card from a column onto a heap), which would trigger win-on-board check to happen again, and that time it would correctly see the win-on-board state and would finish the game.
Modifying the win-on-board conditions was the first task I set out to accomplish. I started by modifying my “performWinOnBoardCheck” method to only check all the columns for face down cards. Now, if a win-on-board state is detected, the GameMaster will then perform the win-on-board actions of autocompleting the game. In doing this it parses through all of the heaps to determine which heap has the lowest value card on top, then it parses through all the other card structures to find that card. Once it finds the card it moves it to it’s respective heap, check to see if win conditions are met, and if they’re not it loops through that algorithm. Since my previous win-on-board conditions included only one card being in the stack, and no cards being in the deck, the win-on-board actions algorithm wasn’t set up for that not to be the case. So I refactored that method to also check each card in the deck and each card in the stack for the card in question.
With that done, I then focused on the problem of checking the win-on-board conditions at the appropriate time. I had been calling the checkWinOnBoard method whenever a card movement was pushed onto the CardMovement heap (this is used by the undo functionality, and basically maintains a limited number of turns in a heap so that those turns could be undone if needed). I realized that, since now the only thing driving a state change in the win-on-board conditions was whether all column cards were face up (and that can only change when a card movement is made that removes a card from a column), I stopped checking these conditions at that place, and instead moved the check to within the DragListener that is applied to cards inside the column. When a card is moved into a column it gets a DragListener applied to it that details how the card should behave (ie it should drag if the user clicks and drags it, when the drag is released it should check the strucutres inside it’s bounds to look for a legal move, if it can’t find a legal move, it should snap back to a particular place, etc). When a card stops being the bottom card in a column, that listener gets cleared and a new listener gets applied that details how the cards should move in a column (if you click and drag a column card that is not at the bottom of the column). Since this DragListener details the behaviour of column cards, and since only column cards can drive a state change in the win-on-board conditions, I simply moved the check to within the DragListener. This fixed the defect of the check happening at the wrong time since the DragListener also detailed how and when the card on the column that we were removing the card from should flip it’s bottom card face up.
That’s most of what I’ve accomplished this week on the game. Again, a lot of this work involved “relearning” the code base. While I would’ve liked to have accomplished more, unfortunately my family and I have been having a bout of sickness over the last couple of days, and that’s taken priority to this kind of work. Also, I apologize if I have rambled too much in this post, I’m also sick as I type this and perhaps my head is not in the best place right now for this kind of work.