• 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:
  • Tim Cooke
  • Campbell Ritchie
  • paul wheaton
  • Jeanne Boyarsky
  • Ron McLeod
Sheriffs:
  • Paul Clapham
  • Devaka Cooray
Saloon Keepers:
  • Tim Holloway
  • Carey Brown
  • Piet Souris
Bartenders:

Tic-tac-toe Design

 
Marshal
Posts: 80493
455
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I would certainly create a Square class. And a GameStarter class, which I shall write because its structure is obvious. Well, almost obvious:-I think one of the sets of ??? should be replaced by a package name. Should the class be public or package‑private? Should the other classes be public or package‑private? Should the other classes be top‑level classes or nested classes? Should all the classes be in the same package?
If you don't use a package name, consider making the class names more specific; “Game” is a bit non‑specific.
 
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
Oh! Sorry.. I should've written the game starter class, but since I was having the UI I wrote last time in mind, it didn't occur to me. I just thought I'd use the same UI class to trigger the game.

And, I can't still figure out how the Square class will help. What am I missing?
 
Campbell Ritchie
Marshal
Posts: 80493
455
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Your board consists of nine Squares.
There are several different ways to arrange those nine squares, but try the individual squares out first. The square class will probably be quite simple.
How are you going to set the symbol on each square? What are you going to do if somebody mistakenly tries to go for an occupied square?

By the way: try these symbols: × is × and ○ is ○
 
Campbell Ritchie
Marshal
Posts: 80493
455
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Sorry, I gave you the wrong symbol first and had to correct the numbers.

Get the application working at the command line first, and consider putting a GUI on top of that later. Make sure the classes have (or at least one of them has) a proper public API which the GUI can use.
 
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:There are several different ways to arrange those nine squares, but try the individual squares out first.


I don't understand what you mean by different ways to arrange the squares Can't they only be arranged as 3 rows and 3 columns?

Campbell Ritchie wrote:How are you going to set the symbol on each square? What are you going to do if somebody mistakenly tries to go for an occupied square?


I thought I'd have a 2-d array to store the whole grid. Will using an array become a problem?

Campbell Ritchie wrote:By the way: try these symbols: × is × and ○ is ○


Is there a significance for using these symbols? Can't I just use "X" and "O" instead?
 
Campbell Ritchie
Marshal
Posts: 80493
455
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
There is no such thing as a 2-D array. It is an array of arrays. A 3×3 array is one way to organise the squares, but there might be other ways. A List, a List of Lists, a 1-D array. All are possible. Until you have considered other designs, you do not know that you have got the best design.
× and ○ might look better than the ordinary letters. That's all.

You want a SquareDemo class which tries things with a Square object, setting its symbol and displaying it. When you have that working, and have decided what to do when somebody tries to use the same Square twice, you can move onto the next class. SquareDemo can then disappear into cyber‑limbo never to be seen again. That lot will take ages. Well over 5 minutes.
 
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, Campbell. I will start working on the Square and SquareDemo classes and post them soon
 
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 designed the Square class. Kindly take a look.



 
Campbell Ritchie
Marshal
Posts: 80493
455
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
There was only one bit I liked and you have gone and removed that bit
 
Campbell Ritchie
Marshal
Posts: 80493
455
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You haven't tested it until you try this lot:-
 
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 actually wanted to use enum, but I am a bit confused. Since I'd be using enum with the Player class, I thought there's no way to pass an illegal character to the Square class. I know my thinking is wrong here but I can't explain to myself why.

Now, I don't know how to receive a 'char' as input and check if it is one of the enum constants. Below is my code that receives a String and verifies if it is an enum constant. Should my setSymbol receive a String? I am confused

 
Campbell Ritchie
Marshal
Posts: 80493
455
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Prasanna Raman wrote:I actually wanted to use enum, . . .

Quite right. You want there to be exactly two values, o and x, so you can create an enum with those two elements. Then you get exactly those two values.

Now, I don't know how to receive a 'char' as input and check if it is one of the enum constants. . . .

Why do you want a char? You can use an enum element. Read about enums in the Java Tutorials, and you should see how easy they are to use. You can enhance an enum by overriding toString or other methods, adding fields and constructors, etc., etc. But in this simple case you might not need to.

Note: all enums implicitly extend the Enum<T> class. Read about that, because that means there are some potentially useful methods which become available. Back to your original enum, and try that out.
Note: if you have not initialised the symbol in the Square class, it will point to null. You should take that into consideration in a getSymbol method.
It says in the Java Language Specification that you can use the == operator instead of equals() on enum elements.
 
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, Campbell. Sorry, I have corrected the Square class now. Please check.

So now, we are limiting what could be passed to the Square class as opposed to checking if the passed symbol is valid. Is my understanding correct?

I don't understand what you've said about null. Why should I check that in getSymbol()?


I've also designed the Board class. Please check if this is right. I now have the Player and the Game classes to complete.
 
Campbell Ritchie
Marshal
Posts: 80493
455
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Prasanna Raman wrote: . . . So now, we are limiting what could be passed to the Square class as opposed to checking if the passed symbol is valid. Is my understanding correct?

Yes, but it would be better to say you have designed a type to match the Square class and the game's rules, and you are using that.

I don't understand what you've said about null. . . .

That is because you have not tested your class properly.

Let's change your demo class a bit:-Why have you got the isOccupied field? You realise that is not necessary? You can delete that field altogether and still have your class work correctly. But you need to think about the design of some of the methods.

You realise that line 12 in my version of the demo class does nothing useful? You get the symbol but never do anything with it; the reference to the Symbol object on the stack will simply disappear into cyber‑space at the end of the main method, never to be seen again.
 
Campbell Ritchie
Marshal
Posts: 80493
455
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Prasanna Raman wrote: . . .
. . .

Yes, I like it. See how easy it is to create such an enum!
 
Campbell Ritchie
Marshal
Posts: 80493
455
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I would prefer to use an if-else when you check that the field is already occupied, rather than return;
You may have problems in future. What you have done is not accept a O/X, but the Game class will not know that an invalid move has occurred, and will allow the other player two turns consecutively.
 
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 removed the isOccupied attribute and also replaced the return statement with an if-else. I realize that the getSymbol() method returns null if it's an empty square. But I thought the method that calls getSymbol() will check for null. Is that not the right approach? Why? Should the return value of a method never be null?

OK, now if I need to avoid returning null from getSymbol(), what do I need to do? Something like adding another enum constant to represent an empty square in the enum class and then setting all the squares to empty in the Square constructor?
 
Campbell Ritchie
Marshal
Posts: 80493
455
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I like the updated versions. But there are still a couple of things I am not happy about, and you seem doubtful about the same things.

A class should be responsible for itself, so beware of returning null and expecting other code to deal with it. If you can avoid returning null from any method, so much the better. If the client code does not “know” it might receive a null, there is a risk of that null propagating itself all round your application until you suffer a NullPointerException thousands of lines away from where the null originated.

One way round it is to change the enum, by adding BLANK. Then you can set the initial state to BLANK. You can override the toString method so in BLANK it returns " " or the empty String. You have to ensure that the setSymbol method does not accept BLANK, or is never sent BLANK. You might be able to program the Game class so the Players never have BLANK as a token. That is one way to do it. I see you have thought of that possibility already.

What is going to happen if it is X's turn and he chooses an already occupied square? The square will not be updated, but the Game will move on to O's turn and X will miss a go. How are you going to signal that mistake back to the Game 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, Campbell. So, the 2 things I need to address now are

  • Avoid returning null
  • Notify the Game class that the move hasn't happened


  • Avoid returning null: I'll try the BLANK enum method.
    Notify the Game class: Can I throw an exception from the setSymbol method?
     
    Campbell Ritchie
    Marshal
    Posts: 80493
    455
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Getting better
    You should be able to override toString on BLANK/EMPTY or whatever to return a space or empty String.
    Yes, you can throw Exceptions. That is one way to sort out that problem. There is at least one other way.
     
    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 Please review my updated classes.

     
    Campbell Ritchie
    Marshal
    Posts: 80493
    455
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Getting better, a lot better

    You should not use if blocks like that in toString(). Non‑object‑oriented code. Difficult to extend. Explore the tutorial about enums again, and find out how you can pass arguments to their constructors.
    BLANK(" ");
    I do not know whether you need to pass "O" or "X" to the others, or whether you can overload the constructor.

    I suggest you consider creating a (checked) Exception maybe called NoughtsAndCrossesException, then make all your other Exceptions subtypes of that. Then you can writeGive your Exception as many constructors as its superclass (Exception) has. Give the parameters the same names as in Exception. Then anybody who is accustomed to using Exception can easily use your custom Exceptions.
    Also consider throwing an IllegalArgumentException if anybody passes BLANK/EMPTY, because that may need to be handled by changing the code to prevent BLANK/EMPTY being used at all.

    There are at least two other things you could do, in addition to or instead of throwing those Exceptions. I challenge you to tell me what they are, and work out whether they are better solutions that Exceptions, or worse.
     
    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 updated the classes based on your suggestions. I don't understand what other exceptions can occur apart from IllegalArgument and IllegalMove in the game. Why would we need a general NoughtsAndCrossesException?

    As for alternatives to exceptions, I am thinking along the lines of notifying the calling method by using boolean return values or exit status. I will continue to think about other ways if this is not what you had in mind but just wanted to ask you if I am way off.
     
    Campbell Ritchie
    Marshal
    Posts: 80493
    455
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    When you changed the Symbols enum, did you print all three elements? If you did, you will see how easily you can obtain the desired behaviour, don't you. You can miss out the access modifier for enum constructors, because it is implicitly private.
    Don't include unchecked Exceptions in the throws clause. You should usually avoid catching unchecked Exceptions in real production code. You must however include them in a @throws tag in the documentation comments. (There is an alternative to @throws. I think it is @exception) Have you worked out where the most appropriate place to catch the IllegalMoveException is?
    Did you work out what the two alternatives to throwing an Exception were that I was thinking of?

    I think you have got the Sqaure cracked; let's try Board.
     
    Campbell Ritchie
    Marshal
    Posts: 80493
    455
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    And it's getting better
     
    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:When you changed the Symbols enum, did you print all three elements? If you did, you will see how easily you can obtain the desired behaviour, don't you.

    Yes, I was actually going to thank you for making me read up on enums. I did print the enum elements and it was all so easy

    Campbell Ritchie wrote:Don't include unchecked Exceptions in the throws clause. You should usually avoid catching unchecked Exceptions in real production code.

    Oh, OK! You're referring to the IllegalArgumentException? So you had me include this exception instead of IllegalMoveException for BLANK inputs because this could be prevented by the programmer? Is that the difference we need to have in mind when creating exceptions to decide between checked and unchecked?

    Campbell Ritchie wrote:Did you work out what the two alternatives to throwing an Exception were that I was thinking of?

    Yes, I am thinking along the lines of notifying the calling method by using boolean return values or exit status. I will continue to think about other ways if this is not what you had in mind but just wanted to ask you if I am way off.

    Campbell Ritchie wrote:Have you worked out where the most appropriate place to catch the IllegalMoveException is?

    Not yet. But should it be in the Game class?

    Campbell Ritchie wrote:I think you have got the Sqaure cracked; let's try Board. And it's getting better

    Thank you I'll work on the Board class and post it soon.

    Finally, I don't understand what other exceptions can occur apart from IllegalArgumentException and IllegalMoveException in the Tic-Tac-Toe game. Why would we need a general NoughtsAndCrossesException?
     
    Campbell Ritchie
    Marshal
    Posts: 80493
    455
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Yes, you are right. Most unchecked Exceptions need to be sorted out by altering the code. If you get an IllegalArgumentException there, that is because you have somehow managed to pass BLANK from a turn. The correct way to sort that out is to go back to whichever code passed the BLANK, and alter it so you can only pass O/X.
    If you get an IllegalMoveException, the correct way to sort that out is to revert to the previous state of the board and ask the player to try again. That can be done at runtime. The reason for making that a checked Exception is to force calling code to handle it. I think you are right about handling that in Game, so you may have to rethrow that Exception in Board.
    The reason for a general NoughtsAndCrossesException is that you might need other sorts of Exception later on.

    Have you worked out what I thought the alternatives to throwing those Exceptions were? If so, which do you think is the best solution?
     
    Campbell Ritchie
    Marshal
    Posts: 80493
    455
    • 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 for making me read up on enums. . . .

    You're welcome
     
    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 worked out what I thought the alternatives to throwing those Exceptions were? If so, which do you think is the best solution?

    Yes, I am thinking along the lines of notifying the calling method by using boolean return values or exit status. I will continue to think about other ways if this is not what you had in mind but just wanted to ask you if I am way off.
     
    Campbell Ritchie
    Marshal
    Posts: 80493
    455
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Not using exit status; that is only available after the app finishes. But returning false is an alternative to the checked Exception.
    The other way I thought of was thisYou can legitimately say that if you have instructed the user not to pass BLANK, and he still does so, then it is his fault not yours.

    I have show you the three options I thought of. Have you got any more? Which of the options do you think is the best?

    Did you consider this form of no‑args constructor for Symbols?
     
    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:Which of the options do you think is the best?

    In this case, I think only using exceptions allows us to notify the Game class of all possible errors (like IllegalMove, IllegalArgument etc.) If we use a boolean return type, the game would just know that the move has failed but not why it failed. In the other option that you showed, would the result of the game at the end not be wrong if we just allowed the "dumb" users to continue playing?

    Is my thinking right? What would you do and why?

    Campbell Ritchie wrote:Did you consider this form of no‑args constructor for Symbols?

    I tried this, but unable to make it work
     
    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 is my Board class:
     
    Campbell Ritchie
    Marshal
    Posts: 80493
    455
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Good start
    I see you are restricting yourself to a 3×3 board.
    Consider changing that to SIZE×SIZE where SIZE is 3. That allows you later to enhance the class to permit a 4×4 or a 5×5 board. Remember you can never have a board smaller than 3×3, otherwise the first player always wins. If you use size * size, you can always be sure of getting a square board, not one with eleven squares and don't use size > 46340!
    I think you will need lots more methods.
    Consider a display method, which prints the current state of the board to the command line/terminal. You might only use that before you write a GUI.
    Consider overloading the set method to take row, column, symbol as parameters.
    Consider using 1 for the top row; most users are not used to starting with 0.
    Consider using 'a' 'b' 'c' for the rows and 1 2 3 for the columns. Remember a char is a number and you can do arithmetic with chars.
    Good to see documentation comments, but the description part needs to go before any of the @ tags. Add some more description to the tags, e.g
    * @throws IllegalMoveException if an already-occupied square is mistakenly chosen.
    You may need a squareCount field, which counts how many squares have actually been used.

    Work out whether you will decide that the Game has finished in an object of this class or somewhere else. If here, think about how to signal completion.
    Think whether there are other ways to organise the Squares than an array.

    Write a BoardDemo class which tests the Board, and even tries some errors so you can see the workings of the IllegalMoveException.
     
    Campbell Ritchie
    Marshal
    Posts: 80493
    455
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Prasanna Raman wrote: . . . In this case, I think only using exceptions allows us to notify the Game class of all possible errors (like IllegalMove, IllegalArgument etc.) If we use a boolean return type, the game would just know that the move has failed but not why it failed. In the other option that you showed, would the result of the game at the end not be wrong if we just allowed the "dumb" users to continue playing?

    Is my thinking right? What would you do and why?


    I might return false instead of IllegalMoveException but use IllegalArgumentException because you get faster performance without the Exception. When faster performance means 0.1μs instead of 0.2μs and you are taking 5s to choose Squares, it doesn't make much difference. I did once write a noughts and crosses app using only a 3×3 board, but I can't find it at present and can't remember what I did about such errors.
    Another possibility is to put something in the ActionListener in the Swing app which tests the displayed square before passing the symbol to the Game in the first place. You can have a JOptionPane appear which tells you move not available.

    What you are learning is that there are several “right” ways to do most things in programming. The right thing to do is have a good explanation, which you showed.
    Yes, the option of simply writing the instruction in the documentation comment is at least overoptimistic, and can permit the players to break the rules. Although that solution does fit the rules of preconditions strictly, I don't like it either.
     
    Campbell Ritchie
    Marshal
    Posts: 80493
    455
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Please use /** for documentation comments not /* There is information about writing documentation comments in Effective Java2nd edition, by Joshua Bloch, and here.
    I looked at the tutorial about enums again; they use Day and Planet in their examples, so maybe you should have called the enum Symbol rather than Symbols.
     
    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:Another possibility is to put something in the ActionListener in the Swing app which tests the displayed square before passing the symbol to the Game in the first place.

    This is a question I've been asking myself several times in the last few days, so I will ask you now. When I've finished writing an application, should it work with any front end? What should be my approach at this stage? Should I try and cover all possible cases and test them in my code itself because I am just starting to learn object oriented programming? Or, should I leave the front end to do a bit of work with the inputs? I am asking not just for this game, but even in general. It's really confusing for me at this stage.
     
    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:I might return false instead of IllegalMoveException but use IllegalArgumentException....

    So, whoever is writing the Game class must know from your documentation that this method returns false only when an illegal move has happened?

    Thank you for pointing out enum name and the documentation. I will make those changes. Learning a lot, can't thank you enough
     
    Campbell Ritchie
    Marshal
    Posts: 80493
    455
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Prasanna Raman wrote: . . . So, whoever is writing the Game class must know from your documentation that this method returns false only when an illegal move has happened?

    Yes. You should be writing that sort of thing in the documentation all the time.
    You would presume that returning true means a correct move has occurred. Look at something like Set#add(E) which has a similar return value.

    Thank you for pointing out enum name and the documentation. I will make those changes. Learning a lot, can't thank you enough

    You're welcome

    Since you have got Square working with the Exceptions, don't change that. But it is important to consider all possibilities, so you can work out which is best.
     
    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 are my board and board demo classes:
     
    Consider Paul's rocket mass heater.
    reply
      Bookmark Topic Watch Topic
    • New Topic