suchja / awesome-qt-snake

A portfolio and learning project show casing my OOP, OOAD, Qt6 and C++ skills.
The Unlicense
0 stars 0 forks source link

As a new player, I want to see the initial game board with all important items so that I can get a first idea what the game is about without reading anything. #5

Closed suchja closed 4 weeks ago

suchja commented 1 month ago

Notes

Acceptance criteria

suchja commented 1 month ago

Adding this to milestone 0.1 as it relates to the core game.

suchja commented 1 month ago

Design

What is the easiest implementation of this that would align with acceptance criteria and NFRs?

Tasks (Implementation)

suchja commented 1 month ago

Design Decisions

snakecore does depend on Qt Core

The core library (now called snake-core) providing the game logic will be based on Qt Core. Although I usually try to fully separate the model (business logic) from technology as good as possible, I prefer to use signals/slots and also the provided "garbage collection".

Dedicated setViewModel method instead of constructor injection

Generally speaking I like dependency injection via the constructor. However, here I'm using a dedicated method on the GameBoardView to inject the dependency to the GameViewModel. Here I currently see the following benefits:

  1. Flexibility in Initialization: By using a setter method (setViewModel), you decouple the instantiation of the GameViewModel from the GameView itself. This separation allows you to initialize and set the GameViewModel independently of creating the GameView.
  2. Late Binding: The setViewModel method supports late binding, meaning you can change the GameViewModel instance associated with a GameView dynamically at runtime if required. - Although I'm not sure whether this is required here at all.
  3. Separation of Concerns: The constructor of GameView should ideally focus on initializing its own internal components and setting up basic functionality. Handling the GameViewModel assignment through a setter method keeps the constructor cleaner.

As I'm not that familiar with this way of injection, it is more like a try. I'll need to come back later on and see whether this really holds true and improves the overall design.

QList vs std::vector (or similar)

For this design it is necessary to pass around lists of points for the snake's tail. Generally speaking this involves usually a maximum of 10-15 objects. Therefore performance consideration isn't that big of a problem. However, as my goal is also to learn about Qt and current versions of C++, I'm deciding to use QList. Although I usually avoid relying on a framework or technology in core libraries, I already decided to use Qt::Core for snakecore. Therefore this isn't as much an issue. Then it seems that especially in Qt6 the QList has been optimized for better memory management. The core feature I see for this scenario is implicit sharing (copy on write). This means an QList instance is only copied, if an element is added. For the snake game an element is only added to the body, if the snake eats a food. Therefore this happens not that often.

suchja commented 1 month ago

Tasks (Implementation) - continued

Open Issues

Testing FoodGenerator (random positions)

As the FoodGenerator uses QRandomGenerator it is difficult to apply automated tests. Problem is that although two consecutive calls shouldn't result in the same position there is a slight probability that it will. If two consecutive calls result in the position, this is not an error per se. It only is an error, if it happens all the time or if the sequence of the created random positions is always the same.

Adding tests on the output of QRandomGenerator seems to be a bad idea. As there is always a slight chance that due to the randomness the tests will fail. In this case I will go without tests, because to me a test that wrongly fails is a real anti pattern. Thus I provide the tests, let them execute and verify the results manually. Then I mark them to be skipped (QSKIP) in the future. Once I have an idea how to properly test this, they should be updated accordingly.

Testing GameViewModel::getFoodPosition()

Generally speaking testing the GameViewModel::getFoodPosition() involves similar problems (regarding randomness) as described in the previous section. Additionally this should test that the coordinates are in the correct coordinates system. Unfortunately some coordinates overlap between the row/column based system applied by the snakecore and the GraphicsScene related system applied by the widgetapp. Therefore I skip these tests as well. Somehow I need to provide a better way of testing this.

suchja commented 4 weeks ago

Tasks (Implementation) - continued

Although I'm not sure about the user experience of this, I'll show the hint in the StatusBar of the MainWindow.