# Crossword game - designing class structure

I've planning to create crosswords game for Android platform. Basically, crossword is a matrix of TextFields (EditTexts) and TextViews. Each crossword might be in american style (first TextView in every question stores question number, and there's no TextView with clue) or swedish (first TextView stores arrow indicating question's 'direction', and there is TextView with clue visible). Every entry (Question + correct answer) might use the same TextField if entries share position for typing letter. Clicking on TextField leads to showing question and higlighting all other TextFields inside entry. User has to change horizontal/vertical entries easily.

This is swedish style: http://finecrosser.com/img/crossword2.jpg and this is american style: http://upload.wikimedia.org/wikipedia/commons/8/86/American_crossword.png

Basic plan for classes:

Views

public class CrosswordEntryView {
private List<TextField> textFields = new ArrayList<TextField>();
private ClueView clueView;

public CrosswordEntryView(ClueView clueView) {
super();
this.clueView = clueView;
}

}

public void highlight() {
for (TextField textfield : textFields) {
textfield.setBackgroundColor(Color.RED);
}
}
}

public class ClueView extends TextView {
}

public class TextField extends EditText {
}


Model:

public class CrosswordEntryModel {
private String clue;
private Orientation orientantion;
private int row, column;
}

public enum Orientation {
HORIZONTAL, VERTICAL;
}

public class CrosswordMatrixModel {
private int width, height;
private CrosswordEntryModel matrix[][];

public CrosswordMatrixModel(int width, int height) {
super();
this.matrix = new CrosswordEntryModel[width][height];
this.width = width;
this.height = height;
}

public void addEntryModel(CrosswordEntryModel entryModel, int column, int row)
throws MatrixOutOfBoundsException {
if (isWrongPosition(column, row))
throw new MatrixOutOfBoundsException(column, row);
this.matrix[column][row] = entryModel;
}

private boolean isWrongPosition(int column, int row) {
if (column < 0 || column >= width)
return true;
if (row < 0 || row >= height)
return true;
return false;
}
}


Controller

public class CrosswordEntryController {
CrosswordEntryModel entryModel;
CrosswordEntryView entryView;

// capturing view's input events, highlighting textfields,
// showing question after focusing, checking if entry is solved, etc.

// should I store entry position's in crossword matrix?
// how can I find controller which shares TextField with this object?
}


Building crossword:

public class CrosswordFactory {
public static CrosswordView build(CrosswordMatrixModel matrix)
{
View root;
// loop through CrosswordEntryModels, look for mutual position for TextFields
// create proper CrosswordEntryView's, add them to root
// if crossword style is swedish - add arrows to TextFields and show TextView
// if crossword style is american - add numbers to TextFields and hide TextView
// create CrosswordEntryControllers ?
return root;
}
}


What do you think about this structure? What if I will find out that I can't use a grid with TextViews and TextFields since bad performance and I will have to switch to ListView and row-based rendering (then using CrosswordEntryControllers to find what and in which row should be displayed might be really painful). How can I try to be prepared for such situation without rebuilding whole app? How should I handle switching from horizontal to vertical entry when users taps on something? Should TextFields be awared of its both parents (vertical or/and horizontal entry)? What are other drawbacks of proposed solution and what can I do better here? I think that I should use AbstractFactory pattern to create swedish/american crosswords based on user's preference, and then use it without changing any code in game's logic. Which design pattern would you use here?

I would really appreciate any tips. Thanks!

-

    // loop through CrosswordEntryModels, look for mutual position for TextFields


You need to have a model of a actual crossword letter grid. (which can initially some object wrapping a char[][])

You need to look for mutual position because you do not have the above grid. You will also need to look for mutual position as user enters answers. because modifying a user's answer for an across question modifies/constrains several of user's down answers, and vice versa.

In the current design, the answers, partial answers or guesses of the user seems to reside in the value of the text fields. The program state should have been encapsulated in the model, that's one of the reasons why changing the user interface implementation details would have such a big impact on the overall software.

Users expect to "write letters to squares" not "enter text". Because they may assume the last letter of a question is "s" if the clue states that the answer is plural. or first letter is something else if all the probable answers start with the same letter. they would one to have a usable way of achieving their intention of entering partial answers.

// create proper CrosswordEntryView's, add them to root


proper CrosswordEntryView you mean align textfields? maybe you should have a grid in your view layer that is a reflection of your grid in the model layer.

    // if crossword style is swedish - add arrows to TextFields and show TextView
// if crossword style is american - add numbers to TextFields and hide TextView


these codes should be somewhere else, preferably in their respective swedish/american packages. something like in the onDraw() method of a SwedishCrosswordView class or SwedishCroswordDisplayStrategy or some such .

These are object construction methods, not object interaction methods:

addTextField(TextField textField)
addEntryModel(CrosswordEntryModel entryModel, int column, int row)


they should not be public methods. In your examples you can just pass in lists of things you want to add one-by-one to the constructor. Also note that your crossword is not in a valid state until all the questions are added. And no more questions can be added one the construction is complete.

public CrosswordMatrixModel(int width, int height, List<CrosswordEntryModel> entryModels) {
super();
this.acrossMatrix = new CrosswordEntryModel[width][height];
this.downMatrix = new CrosswordEntryModel[width][height];
this.width = width;
this.height = height;

//matrix = acrossmatrix or downmatrix depending on entrymodel direction
for(CrosswordEntryModel entryModel) {
// start at the (row, column) of entry model
// move in the direction of entry model
// set matrix[currentrow][currentposition] to entrymodel.
}

}


after construction each question letter square in the matrix points to the question it is contained in. using a single matrix you cannot represent 1-across (0,0,horizontal) and 1-down(0,0,vertical) at the same time. what will the matrix[0][0] would point to?

How should I handle switching from horizontal to vertical entry when users taps on something?

If you plan to user that way of interaction then somewhere in your model classes you should have a current direction property, no.

there is a crossword on yahoo games you can draw UI inspiration from there;)

your current state may include current entry position and current direction. (as well as letters entered so far)

your view representing the current state may consist of a grid of buttons, (table layout or whatever android specific bullshit), for example. button labels show letters entered so far. button at the current position has a border of some color. other buttons that corresponds to the current question have border of some other color. clicking a button at a position other than the current position set the current position to the position of the clicked button. clicking or dblclicking the button at the current position changes the current direction. when current direction, or current position changes displayed clue is updated.

other things you have not mentioned are End-Game logic. Checking whether user have completed the game. etc.

Another important design constraint is where will the crosswords come from. If the puzzle will be designed by humans they will decide what type of puzzle the are preparing at the beginning, since there is not a one to one relationship with swedish and american puzzles. (swedish style crosswords always have top-left square full, whereas american style ones usually don't. Moreover, american crosswords are expected to be symmetrical, although they are not required to be so technically, whereas swedish style ones are usually not.) Abstract factory may not be a sensible thing in that case. Instead you would want something like "get me a random puzzle of some certain type".

-
Thanks, that's amazing.I thought about using AbstractFactory to create TextView's based on crossword's style. –  Piotr Dec 29 '12 at 12:18
One question - how would you connect Views and Models - let's say I am clicking on TextField and write a letter. How do you know which model is it pointing at? Would you create Controller class with reference to Question Box and all TextFields for every entry? Let's say I want to highlight current entry - when would you put this logic? Should I have create another grid of controllers...? Or would you put information about column/row in every view, and then after clicking on it you would search for proper views in view grid and highlight them in more general GameController? –  Piotr Feb 9 '13 at 22:05
One more thing. Could you take a look at this UML diagram: imagebanana.com/view/unf6bjwy/uml.png It's presenting my idea of using Abstract factory in order to make GridViews with TextFields and TextViews based on provided matrix model. What do you think about it? Basically, CrosswordGridViewFactory it's looping through models and creating TextFields/TextViews with selected style. On the end, it's passing it to new CrosswordGridView object. But still I'm not sure if every entry should have seperate controller for borders/highlighting/etc. Thank you for any tips (: –  Piotr Feb 9 '13 at 22:27
and last thing - as I understand, in your solution, you ask CrosswordMatrixModel for a specific model on position x,y and orientation horizontal/vertical, right? Or would you set 'orientation' field inside MatrixModel, and then just ask for position x,y? –  Piotr Feb 11 '13 at 8:13