In the last Tic Tac Toe entry I tried to make the winner more noticeable and tried to pretty up things some. Honestly, I didn't like the end result. So this time I'm going to work on making the winner stand out even more. Last time I blurred the entire grid with the exception of the winning row, column, or diagonal. I still want to keep the blurring effect because I think it's a great way to remove the losing components from focus. However, blurring the entire grid really made everything look ugly and hard to see.
Last time I blurred the losing components by removing the winning GridCells' images and adding them to a winner's node. This time I'm going to also remove the losing GridCells' images and add them to a loser's node, then I'll blur that instead of the whole Grid.
I'm also going to add some color to the winning GridCells and play some fanfare music when a winner is found.
I've chosen to use JFugue to play the music because it allows me to play music from Strings instead of having to record sounds. It also wraps the java sound stuff which can be cumbersome to work with. If you have a chance, or ever need to be able to play music in Java in about 2 lines of code, please check out JFugue. It's a really great API.
Changes to GridCell
First let's take a look at the changes made to the GridCell object.
public class GridCell extends SGGroup { // ... Enum and other instance variables ... // private SGShape rectangle; // ... Constructor and other getters, setters ... // public SGShape getRectangle() { return rectangle; } public void setRectangle(SGShape rectangle) { this.rectangle = rectangle; } }
Basically all I did was change the name of the rectangle from rectShape to rectangle and added some accessor methods.
As you'll see soon, I need access to the GridCell's rectangle so I can change it's color if it's part of the winning row, column, or diagonal.
Changes to GridCellMouseListener
Next let's take a look at the changes I made to the GridCellMouseListener. Here I needed to update the solve method to add winning GridCells to the winner's node and add losing GridCells to the loser's node. I also updated it to change the color on winning GridCell rectangles.
public class GridCellMouseListener extends SGMouseAdapter { // The background color for winning grid cells. private static final Color WINNERS_COLOR = new Color(195, 217, 255); // The music string to play when a winner is found. private static final String WINNERS_MUSIC = "I[64] T160 C6s*3:1+E5s*3:1 C6s*3:1+E5s*3:1 C6s*3:1+E5s*3:1 C6q+E5q G#5q+Db5q A#5q+Eb5q C6i*3:1+F5i*3:1 Ri*3:1 A#5s*3:1+Eb5s*3:1 C6h.+F5h."; // ... Constructor, instance variables and mouseClicked method ... // private void solve(GridCell gridCell) { // ... // SGGroup winnersNode = null; SGGroup losersNode = null; if ((rowCounts[row] == 3) && (Math.abs(rowValues[row]) == 3)) { // there's 3 values at this row and the value is either 3 or -3 // we have a winnerFound. winnerFound = true; System.out.println("Winner " + gridCell.getValue() + " at row[" + (row + 1) + "]"); winnersNode = new SGGroup(); losersNode = new SGGroup(); // Add all the images from GridCells in the same row to the winners node. for (GridCell[] rows : grid.getGridCells()) { for (GridCell sibling : rows) { if (sibling.getRow() == row) { winnersNode.add(externalizeGridCellImage(sibling)); sibling.getRectangle().setFillPaint(WINNERS_COLOR); } else { losersNode.add(externalizeGridCellImage(sibling)); } } } } // ... Basically same thing for columns and diagonals ... // // if a winner was found, the winners node will not be null. if (winnersNode != null) { // add it to the gameScene. TicTacToe.gameScene.add(winnersNode); TicTacToe.gameScene.add(losersNode); // Start up the player in a separate thread so it won't freeze the UI. new Thread(new Runnable() { public void run() { // Play the winners music. Player player = new Player(); player.play(WINNERS_MUSIC); } }).start(); final SGNode finalLosersNode = losersNode; SwingUtilities.invokeLater(new Runnable() { public void run() { // ... create blur ... // blurEffect.setChild(finalLosersNode); TicTacToe.gameScene.add(blurEffect); // start an animation to blur the loser's node. Clip.create(1000, gaussianBlur, "radius", 2f, 4f, 6f, 8f, 10f).start(); } }); } } // ... Update row, column and diagonal counts and values ... // }
Basically in the solve method, I now have a winners' node and a losers' node. I then loop through the GridCells and if they match the winning row, column, or diagonal they get added to the winner's node, otherwise they get added to the loser's node.
In the end, if a winner has been found I add the winnersNode and losersNode to the gameScene. I also start up a Thread to play the music. Player.play(String musicString) waits until the music has been played, so I added it to a separate thread to avoid locking the UI.
I've also added a bit of animation using the Clip object from Scenario. This causes the losersNode to gradually blur.
So, that's pretty much it. I'm musically challenged, so special thanks to my Wife for helping me out with the music. I hope you can recognize it, it's from my favorite video game.

I think I'm tired of Tic Tac Toe. It's time to move on to something more fun.
The source for this part is attached at the end. I don't like duplicating the looping code for rows, columns, and diagonals, so there's an extra source zip that contains some cleaner code. I've created a Matcher object which takes care of all the searching, changing colors and moving nodes around.
Cheers,
Eric
| Attachment | Size |
|---|---|
| tic_tac_toe_5.zip | 93.43 KB |
| tic_tac_toe_5_op.zip | 116.26 KB |