Observer methods

Hello,

Regarding the GameObserver interface, it is clear, that its methods should be called by observable (game), e.g. rowsCompleted(), which informs the observer about one or more row has been completed.

But how could knowing, there are some completed row/s (not a number of them), be useful for the observer, especially Autoplayer?

Another method is “game over”, why the observer (sub-)class needs the method gameView.isGameOver(), as if the observer should query about it, while the game has already promised to inform the observer by calling gameOver() as soon as the game is over?

While you theoretically have access to a copy of the game’s entire state (without the starting seed of course) at any point in time, it would be wasteful to query the entirety of this information after every move.

For example you might want an observer that only logs the number of rows cleared and the time in between clears (i.e. you want to create a statistic, for whatever reason). You do not need to think about the current piece or the state of the board in between cleared rows for this. Hence you could tell your observer to log the current score and time whenever you receive a signal from the observable telling you that rows have been cleared. From this information alone you can then infer number of rows cleared.

Essentially the observer allows you to focus only on those things you care about and thus reduce the number of queries for information you have to perform. I assume that in a real world application (like an actual game) there are probably many listeners waiting to track certain aspects of the application, e.g. certain pieces of the GUI might refresh independently from others and rightly so because not every change of state affects every single component.

2 Likes

Thank you, @Bastian.Heinen, for your response. However, I still want to understand what is the value of knowing just that there are some completed row/s. Why do we have this method exactly and e.g. not the number of the rows?

In other words, if I really care about the number of completed rows as a metric of optimal decision, then why the game doesn’t tell me the number of them?

Are these 4 methods of observer essential to make a decision about the place, on which the current piece should land, or they are rather a mean to check that our observer have full knowledge about the game (as it is used for testing in ObserverTest)?

1 Like

It could be that some observers have access to the entire game state and others only to certain parts and their knowledge of the other parts is restricted to the signals they receive from the observable.

On the extreme end of this we have a mere observer, i.e. any old class implementing the observer interface. Such an observer can not follow the game completely because there is no way for the observer to access the game’s state unless it is granted access to a copy of part of its state. (The observer methods don’t tell you were a piece moved to, only that it moved) Such an observer would not be able to keep track of the score because as you point out there is no way for them to get the number of rows cleared.

I can not tell you why our observer interface only provides these methods. At the end of the day you have to ask whoever implemented it what their intent was.

I would not think too much about why the observer interface is exactly the way it is and more about how you can use the meager information it provides to manage your queries for the game’s current state.

Finally, in order to decide where a piece should land you simply need to know the board and the current piece and then come up with a clever way to pick a landing spot/rotation. (i.e. you need to find a way to search for cavities that fit the current piece in some rotation and a way to find the best one among them)

1 Like

An event is something that is true or false.
Examples:

  • A piece has moved (otherwise it stayed still)
  • There were complete rows (otherwise no row was cleared)
  • Exactly five rows were cleared

As events are always binary in nature, there is only an event for any rows cleared not for an exact number.
An observer now can listen to such an event and when the event is fired, perform the computationally expensive step to compute how many rows were cleared for instance.

This behavior is similar to interrupts (more in the SysArch lecture).

An alternative way to interrupt/observers is polling:
You always (in every time step) look for changes.
For instance, you would have to iterate over the whole board to check if a row was cleared.
But this is computationally expensive and wasteful as the game knows exactly when a row is cleared (it computes such things to function correctly) and can inform others of this fact.
In some cases, it might not even be possible to perform a polling approach.

You are right that theoretically, the event could carry more information like the number of rows or that an observer might subscribe to a 5-rows-cleared event.
But this is more overhead for the game and needs more engineering.

1 Like

Would it then make sence if a new Game is instantiated for every possible state, so I can benifit from Game methods like moveDown() getCompletedRows(). If no, should the board given by GameView be cloned for every state?

If you want to inspect the board, you have to obtain a copy using getBoardCopy (similar for the pieces).
Depending on what you want to do, you could also create new games.

1 Like