• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • Ron McLeod
  • paul wheaton
  • Jeanne Boyarsky
Sheriffs:
  • Paul Clapham
  • Devaka Cooray
Saloon Keepers:
  • Tim Holloway
  • Roland Mueller
  • Himai Minh
Bartenders:

Tic-tac-toe Design

 
Ranch Hand
Posts: 546
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:don't use size > 46340!

I have documented this restriction but should I be throwing an exception instead? Or can this be handled in the UI?

Campbell Ritchie wrote:Work out whether you will decide that the Game has finished in an object of this class or somewhere else.

I think this check should happen in a method(checkWinner()?) in the Game class. Is this correct?

Campbell Ritchie wrote:Think whether there are other ways to organise the Squares than an array.

I considered a few options, but an array of arrays seems the best option to me considering the kind
of operations that need to be performed.
 
Ranch Hand
Posts: 250
1
Eclipse IDE Chrome
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Prasanna Raman wrote:



Generally enum names are singular. X should be of type Symbol in the same way that obj would be of type Object.
 
Marshal
Posts: 80765
488
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Prasanna Raman wrote:

Campbell Ritchie wrote:don't use size > 46340!

I have documented this restriction . . .

46340 is (int)√2147483647!

Prasanna Raman wrote:

Campbell Ritchie wrote:Work out whether you will decide that the Game has finished in an object of this class or somewhere else.

I think this check should happen in a method(checkWinner()?) in the Game class. Is this correct?

Work it out. If you call such a method, what will it return? Does that mean the method must be called after every single move? What if you reach move 9 and nobody has won? What will that method return?
More important: can you think of any other ways to do it? I am not here to tell you how to do it but to make you think

Prasanna Raman wrote:

Campbell Ritchie wrote:Think whether there are other ways to organise the Squares than an array.

I considered a few options, but an array of arrays seems the best option to me considering the kind
of operations that need to be performed.

I presume you mean array because you haven't used an array of arrays. I would not use a List myself, and I think an array or an array of arrays each just as good as the other. But that is only my opinion.

The important thing is to consider several options. When you have considered all those options and made a decision, it is likely thatyou have made the correct decision. If you don't consider lots of options, a decision is likely to be wrong.
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Don't use the int literal 97. Use the char literal 'a' instead.

I thought you were using a 1-D array, but now I see you have changed to Square[][]. So I was mistaken in my previous post about using arrays. Sorry.
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I must have been asleep when I wrote that. It probably won't even compile.Try this instead:-In your testing code, you will only ever get one wrong option; the whole try block will terminate abruptly and you will go straight to the catch.
Don't use 3 in the loop; use size.
Don't use a catch to turn the ArrayIndexOutOfBoundsException to another sort of Exception. Do the arithmetic before you enter the array, so entering row 'z' will create an IAE straight off.
 
Prasanna Raman
Ranch Hand
Posts: 546
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Here's my modified Board class:

Campbell Ritchie wrote:Work it out. If you call such a method, what will it return? Does that mean the method must be called after every single move? What if you reach move 9 and nobody has won? What will that method return?
More important: can you think of any other ways to do it? I am not here to tell you how to do it but to make you think

I am still working on this one.
 
Prasanna Raman
Ranch Hand
Posts: 546
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:

Have you used these lines of code to show that returning the board might be dangerous because its state could be tampered with?
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Prasanna Raman wrote: . . . Have you used these lines of code to show that returning the board might be dangerous because its state could be tampered with?

Yes. I presume you have tried it. It is always worth knowing when you are returning something which might allow the state of an object to be altered unexpectedly.
You will need to return something encapsulating the current state of the board (not a String), so you can display it on a GUI. But it must be safe, prohibiting any changes.
[edit]:add link to “String”[/edit]
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Now youhave to decide whether to get the Board class or another class to signal that the Game has finished.
 
Prasanna Raman
Ranch Hand
Posts: 546
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Yes. I presume you have tried it. It is always worth knowing when you are returning something which might allow the state of an object to be altered unexpectedly.

Yes, I did try this but am very confused Why shouldn't I "trust" the programmer (in this instance, it will obviously be me!) of the class that will access the Board array? The whole application is in a package, and do we need to assume that an evil tamperer can get access to this package? (because the Board could only be modified if you're in the same package) Should we not believe that this package is "safe"?
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You might only be using the program yourself and in this instance you might be the only person using it. But you are probably looking for a job, and you hope to write real‑life systems which real‑life criminals will try to break. You want to be able to tell an employer you know how to design classes securely. It is a part of good software design

And goodbye nice number of posts
 
Prasanna Raman
Ranch Hand
Posts: 546
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:And goodbye nice number of posts

Sorry, does this mean you will not be helping anymore?
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
No, it means it will be a long time before my number of posts reads as a nice number
 
Prasanna Raman
Ranch Hand
Posts: 546
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:No, it means it will be a long time before my number of posts reads as a nice number

Don't worry! At this rate, you should reach 44444 very soon
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It's when you reach 66666 that you really worry!
 
Prasanna Raman
Ranch Hand
Posts: 546
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Work it out. If you call such a method, what will it return? Does that mean the method must be called after every single move? What if you reach move 9 and nobody has won? What will that method return?

I think the method should return a String (or an enum) indicating whether X/O has won, or if it's a tie, or if the game is in progress. The method will be called only if the occupied square count is >=2*size-1.

Campbell Ritchie wrote:can you think of any other ways to do it? I am not here to tell you how to do it but to make you think

I feel it's appropriate to have this check done in the Game class, but I am not able to think of other ways to do this.
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Prasanna Raman wrote: . . . I think the method should return a String (or an enum) indicating whether X/O has won, or if it's a tie, or if the game is in progress. The method will be called only if the occupied square count is >=2*size-1.
. . . I feel it's appropriate to have this check done in the Game class, but I am not able to think of other ways to do this.

Why do you need to check whether you have passed 2*size-1? When you get a complete row, you have completed the game. The constraints on passing O/X and taking turns should ensure that anyway.
Not a String. There are other things you can pass. One would be a GameCompletion object, with boolean completed and Symbol winner fields. You might use null for a Tie. I am sure you can think of other ways to signal game completion. Let's see them, and we can all see which is the best solution.
Why does the Game class have to signal completion? Why can't you do it in the Board class?
Remember these questions are (at least partially) to make sure you are thinking about all the design decisions as you go.

If you return a Symbol[][] from Board#getBoard, that would have no links to the original object, and no outside code could damage it. There are several other ways to secure that class from illegitimate interference.
 
Prasanna Raman
Ranch Hand
Posts: 546
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Why do you need to check whether you have passed 2*size-1?

The minimum number of occupied squares needed for any player to win is 2n-1. So I thought I'd check that first before calling checkWinner() so we avoid calling checkWinner() from the first move.

Campbell Ritchie wrote:I am sure you can think of other ways to signal game completion.

The other way I can think of is by using an enum called Result with 4 states - X,O,TIE,IN_PROGRESS. The checkWinner() method can return one of the 4 states.

Campbell Ritchie wrote:Why does the Game class have to signal completion? Why can't you do it in the Board class?

In my view, it is not the responsibility of the Board class to signal completion by applying the rules of the game. The game class is like the rule book, which should determine the result of the game.

Campbell Ritchie wrote:If you return a Symbol[][] from Board#getBoard, that would have no links to the original object, and no outside code could damage it.

I am very confused with this one. I realize in this instance that returning Square[][] is dangerous because the individual squares could be modified. Does it mean that an object can never be returned? Or the object can't be returned in some instances where its state is modifiable? I am not able to visualise when an object can or can't be returned.

Campbell Ritchie wrote:There are several other ways to secure that class from illegitimate interference.

Can we avoid this interference by placing the Game class in a different package?
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Prasanna Raman wrote: . . . So I thought I'd check that first before calling checkWinner() so we avoid calling checkWinner() from the first move.

What is wrong with checking from the first move? Obviously the first 4 turns you will always get false. I think it is simpler code, but it is your app, so you will have to decide. But consider several solutions before you decide.

. . .The other way I can think of is by using an enum called Result with 4 states - X,O,TIE,IN_PROGRESS. The checkWinner() method can return one of the 4 states.

You mentioned a different way to signal completion about 28000 posts ago, in this very thread.

. . . In my view, it is not the responsibility of the Board class to signal completion by applying the rules of the game. The game class is like the rule book, which should determine the result of the game.

Agreed, then.

. . .

Can we avoid this interference by placing the Game class in a different package?Probably. I would have put all the classes you have at present into one package. Then you can restrict access to the other classes by giving them package‑private access. You will probably have to give Game and Symbol public access.
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Prasanna Raman wrote: . . . I realize in this instance that returning Square[][] is dangerous because the individual squares could be modified. . . .

Design problem: the implementation which relies on a mutable object. Like this version of my Kettle class. The minFill field represents the smallest amount of water required to cover the element.What you are doing is passing a mutable object in. This mutable object becomes part of the implementation of Kettle, but because of bad design in the fill method, you now have two references to the same object. So when you set the w object to freezing, the kettle cools off. Later on you are returning the same object, so you now have three references to it: w, ww and one inside the Kettle object. When you change the volume of the ww object, you enlarge the object inside the Kettle so it would cause it to overflow. At least you would have cold water all over the floor, not boiling water running over your hands.

General principle. This is similar to creating alias references. If you have a mutable reference type which constitutes part of the implementation of your object, and you let it escape, your object is at risk. If anybody changes the state of that mutable object, the state of your whole object can change. Arrays are implicitly mutable, because you can always write myArray[i] = xyz;
You were returning the Square[][] array which forms part of the implementation of the Board class. So anybody getting access to that array can alter the state of the Board.
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Now, try this version of Kettle. You should find those errors have vanished. The reason is that fill no longer simply copies the w object across; it takes a defensive copy, which is exactly what getContents does. If your getBoard method had taken a defensive copy, that would prevent such interference. Alternatively, my suggestion of a Symbol[][] would prevent interference, because it is not a field, but only a local variable; once the method returns, it goes out of scope and there is nothing to interfere with.
You are going to have to create an array which is a different object from the original array. As you know, all arrays have a clone() method, but that only creates a shallow clone. So that won't work. You can clone the individual 1D arrays, or there is probably a method in the Arrays class which does a deep clone for you.
There are probably many other ways to safeguard the data inside the Board object.
 
Prasanna Raman
Ranch Hand
Posts: 546
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you very much for the example and the explanation. It was very helpful I think I understand it a lot better now, but need to get one more thing clarified

As a rule, should I just refrain from passing mutable objects from any method, or may there be instances when you do pass mutable objects safe in the knowledge that it's not going to be accessed from within the same package? I ask this question because the Kettle example you gave and the Board scenario we have here do give the object away, but those objects can only be modified if the evil tamperer has access to the package we're in. I'd think it's the former because if I make sure that I don't ever pass any mutable objects from my class, then I don't have worry about who's getting access to my object, whether he has access to this package or not etc. Is my understanding right?
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Prasanna Raman wrote:Thank you . . .

You're welcome

As a rule, should I just refrain from passing mutable objects from any method, or may there be instances when you do pass mutable objects safe in the knowledge that it's not going to be accessed from within the same package? . . .

Opinions would differ. If you can seal your package and are sure there is no naughty code around, then you will be all right. That is one opinion.
I am however a confirmed believer in defensive programming. If anything can go wrong, your fellow‑programmer will do it. So program defensively: throw IllegalArgumentExceptions or NullPointerExceptions from constructors when wrong arguments are passed. Take defensive copies of mutable objects.

It also means you can go to a prospective employer and talk coherently about defensive programming and securing your classes.
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I don't know whether that software design book discusses defensive programming.
 
Prasanna Raman
Ranch Hand
Posts: 546
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you Here is my updated Board class:I am still thinking about ways to signal game completion.
 
Ranch Hand
Posts: 133
Hibernate Oracle Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
To check for game completion -

You can wait until a certain number of moves are done. (at least 4 : 2 by each player)

Check what are all the combinations that make a game complete. (in terms of the object grid ) - after every move.
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
There is an easier way to calculate sizes for the update method; row - 'a' > size - 1 is awkward. Work out what would happen if you subtract the character before 'a', which you can find in the Unicode Charts.
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Change the print call in display State to print Symbol + " | "
Change the println call after each line to println("\b\b "), which should get rid of the excess |
Work out how to print a line of --------- characters.
Or, even better, find the box drawing characters in Unicode.
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Now you can pass O or X and you can display the board nicely on the command line. Now you need to get the Game class working. I think it needs a public interface like thisWhen you have that public interface, then you can accept input from a GUI, and since it is coderanch, you can have the GUI classes in a different package.

What do you think the Game class will do?
 
Prasanna Raman
Ranch Hand
Posts: 546
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you. I've changed the 'size - 1' part but haven't fully drawn the board yet using Unicode characters. Here is my updated Board class:Should I do Player next, and then Game after that?
 
Campbell Ritchie
Marshal
Posts: 80765
488
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Neither nor. At least that is my opinion and I might not be correct. I think you should work out on paper what the algorithm is for deciding whether the game has been won lost or tied. Also the algorithm for taking turns. Consider whether your algorithms can be scaled to 4‑squared boards or larger.
 
Prasanna Raman
Ranch Hand
Posts: 546
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
OK! I'll work on these algorithms then.
 
Prasanna Raman
Ranch Hand
Posts: 546
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
  • Win:Check all rows, columns and diagonals for the same symbol. If there's a symbol occupying all rows/columns/diagonals, then the player with that symbol has won
  • Tie:Occupied square count equals size*size and the previous condition is not satisfied
  • To check turns:Record symbol used in first turn and mark it as player1's symbol. Mark the other symbol as player2's. Always check if the count of the symbol used in the first turn is 1 greater than the symbol used in the second turn in which case it is player2's turn. If it is equal it is player 1's turn. Any other relation between the counts implies that one of the players has tried to play out of turn.
  •  
    Campbell Ritchie
    Marshal
    Posts: 80765
    488
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Not convinced. You already know which the winning symbol (if any) is. How are you going to identify a row column or diagonal? Agree about tie, however.
    You are making sure no player can play out of turn, so why do you need to check. There is a much much simpler way to take turns.
     
    Prasanna Raman
    Ranch Hand
    Posts: 546
    1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    To identify a row column or diagonal, I am thinking of using separate 'for' loops to iterate over the rows, columns and diagonals. If any of these rows, columns or diagonals contain the last entered symbol, the player with that symbol has won.
     
    Campbell Ritchie
    Marshal
    Posts: 80765
    488
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    But how are you going to determine whether a particular square is in a row column bend or bend sinister? You can find what bends are here.
     
    Prasanna Raman
    Ranch Hand
    Posts: 546
    1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Sorry, I think I am missing something here If I am going to check all rows, all columns and all diagonals for the same symbol, why do I need to determine where a particular square is?
     
    Prasanna Raman
    Ranch Hand
    Posts: 546
    1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I've been thinking about this, but unfortunately I am going nowhere Please help.
     
    Campbell Ritchie
    Marshal
    Posts: 80765
    488
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Draw a grid on paper, with the row and column numbers in. Start with (0, 0) at the top left rather than (a, 0). See if you can work out what features rows columns “bends” and “bends sinister” have in common. Look at the numbers.
     
    With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
    reply
      Bookmark Topic Watch Topic
    • New Topic