Presentation is loading. Please wait.

Presentation is loading. Please wait.

9-May-15 Notes on Style Testing the TicTacToe game.

Similar presentations


Presentation on theme: "9-May-15 Notes on Style Testing the TicTacToe game."— Presentation transcript:

1 9-May-15 Notes on Style Testing the TicTacToe game

2 A first approach I want to create a large number of tic-tac-toe partial games for testing purposes I could do it this way: char[][] array; array = new char[][] { { 'X', ' ', 'O' }, { ' ', 'X', ' ' }, { 'O', ' ', 'O' } };...and I could reset the array for each tic-tac-toe board I want to use as input This looks nice, and is easy to read, but it is a real nuisance to type out a lot of these

3 So I did this instead setBoard(" o x o o"); private void setBoard(String xxx) { xxx = xxx.toUpperCase(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { array[i][j] = Character.toUpperCase(xxx.charAt(3 * i + j)); } } } Now it's a lot easier to create tic-tac-toe boards for testing

4 Morals Use methods to make your life easier If something is ugly, hide it in a method Also... While our main goal should be to write programs that are easy to read, it isn’t our only goal The best thing to do with hard-to-read methods is to rewrite them Second best is to explain them in comments I didn’t include the comments on the slide, but they are in my code!

5 Refactoring Refactoring is reorganizing a program without changing what it does Refactor in order to: Make a program easier to understand Make a program easier to modify

6 Before refactoring public final void testMakeCornerMove() { setBoard(" oxoxxoxo"); computerPlayer.makeMove(board); assertBoardIs("xoxoxxoxo"); setBoard("oo xxooxx"); computerPlayer.makeMove(board); assertBoardIs("ooxxxooxx"); setBoard("oxoxxoxo "); computerPlayer.makeMove(board); assertBoardIs("oxoxxoxox"); } I seem to be doing the same thing over and over...

7 After refactoring private void beforeAndAfterMove(String before, String after) { setBoard(before); computerPlayer.makeMove(board); assertBoardIs(after); } public final void testMakeCornerMove() { // Center and all other corners taken beforeAndAfterMove(" o x o o", "x o x o o"); beforeAndAfterMove("o x o o", "o x x o o"); beforeAndAfterMove("o o x o ", "o o x o x"); beforeAndAfterMove("o o x o", "o o x x o"); // Corner move is all that's left beforeAndAfterMove(" oxoxxoxo", "xoxoxxoxo"); beforeAndAfterMove("oo xxooxx", "ooxxxooxx"); beforeAndAfterMove("oxoxxoxo ", "oxoxxoxox"); beforeAndAfterMove("xxooxxxoo", "xxooxxxoo"); }

8 Moral The DRY principle: Don’t Repeat Yourself Every piece of data should have a single unique representation “A man with a watch knows what time it is. A man with two watches is never sure.” -- Segal’s Law Example: If you have a measure of distance, don’t keep it in two variables, distanceInFeet and distanceInMeters -- keep it in one variable, and use a method to convert to the other units as needed Each nontrivial operation should be represented by a unique piece of code Don’t “cut and paste” code--turn it into a method Variations in code can often be handled by a parameter list Corrections and updates are much simpler

9 Testing for a winning move Here’s one way to test for a winning move: if (board.get(1, 1) == 'X' && board.get(1, 2) == 'X' && board.get(1, 3) == ' ') { board.set(1, 3, 'X'); return true; } There are 24 combinations to test for This is why I made testing for a winning move optional! Using a method would help some if (winningMove(1, 1, 1, 2, 1, 3)) return true; But that’s still 24 error-prone lines, plus a method

10 A bright idea For each location on the tic-tac-toe board, Put an 'X' in that location Check for a win (with our computerHasWon() method) If it’s a win, that’s our move Otherwise, put a blank in that location, and keep trying We can do something very similar for testing if we need to make a blocking move

11 The code private boolean makeWinningMove(TicTacToeBoard board) { for (int i = 1; i <= 3; i++) { for (int j = 1; j <= 3; j++) { if (!board.isEmpty(i, j)) continue; board.set(i, j, 'X'); if (board.computerHasWon()) return true; board.set(i, j, ' '); } } return false; } This code works, but...

12 An unexpected consequence Row 1, column 1 is already taken. Row 1, column 2 is already taken. Row 2, column 1 is already taken. Row 2, column 1 is already taken. Row 2, column 3 is already taken. Row 1, column 2 is already taken. Row 2, column 1 is already taken. Row 1, column 2 is already taken. Row 2, column 1 is already taken. Row 2, column 3 is already taken. Row 3, column 2 is already taken. Why did this happen? I did check for a blank space before placing my 'X'

13 In the set method of TicTacToeBoard if (board[row - 1][column - 1] != ' ') { error("Row " + row + ", column " + column + " is already taken."); } I can only “set” a location if it is initially blank I never thought about “erasing” an X or an O Proposed solution: Modify the set() method Problem: I asked you not to modify the provided methods Under these constraints, my “bright idea” cannot be made to work :-(

14 Morals Insofar as possible, methods should do a single thing In particular, it’s usually a bad idea to mix computation and input/output in the same method If you mix computation and input/output in the same method, then you can’t do the computation without also doing the input/output Example: In a previous assignment I specified methods findDayOfWeek to only do computation, and findAndPrintDayOfWeek to call the former and print the results This allowed me to test your computations without getting a bunch of output

15 Fix #1 for board.set(row, column, ch) I could have made set return a boolean -- true if the location was set, false if it wasn’t boolean set(int row, int column, char ch) { if (board[row - 1][column - 1] == ' ' && (ch == 'X' || ch == 'O')) { board[row – 1][column – 1] = ch; return true; } else return false; } Disadvantage: The user might not check the result Disadvantage: I test for two things that could go wrong (location is taken, bad character) and this doesn’t distinguish between them

16 Fix#2 for board.set(row, column, ch) I could assert that the location is available, and assert that the character is legal void set(int row, int column, char ch) { assert board[row - 1][column - 1] == ' '; assert ch == 'X' || 'O' ; board[row – 1][column – 1] = ch; } Disadvantage: Bad use of assert --it should be used for things you believe to be true, not for error checking

17 Fix#3 and #4 I could throw an Exception for each error condition This is the best solution We haven't covered Exceptions yet (I nearly forgot this one) I could just skip error checking Big disadvantage: No warning to the user if something is wrong

18 The End


Download ppt "9-May-15 Notes on Style Testing the TicTacToe game."

Similar presentations


Ads by Google