Lab 11: Parallel and Asynchronous Programming #
Starting code #
- Examples/
- GameOfLife/
- .gitattributes
- .gitignore
- GameOfLife.UI/
- App.axaml
- App.axaml.cs
- Assets/
- Controls/
- Converters/
- Examples/
- GameOfLife.UI.csproj
- Models/
- Program.cs
- Services/
- ViewLocator.cs
- ViewModels/
- Views/
- app.manifest
- GameOfLife.sln
- task.en.md
- task.pl.md
Simulation of Conway’s Game of Life – Responsive UI and Parallel Computations #
The goal of this task is to complete fragments of the logic for a desktop application that simulates the cellular automaton “Game of Life”. The main focus of our application is not graphics itself, but rather application performance and responsiveness.
Description of elements that make up the desktop application. Everything mentioned here is already implemented. Your “part” begins in Stage 1.
The application can be divided into 3 parts:
- Left panel: This area is used to control input data.
Choose directory...button: Opens a system folder selection dialog.- Progress bar: Shows the progress of asynchronous file scanning.
- File list: Displays files found in the folder. Selecting an item from this list immediately stops the current simulation and starts a new one for the selected file.
- Simulation area: Main part of the screen where the game is rendered.
- Bottom status bar: This area is used to monitor performance and control the simulation progress in real time.
- Status: Displays messages:
Choose directory...,Scanning directory...,Done.andSimulation: <filename>. - Speed slider: Allows you to dynamically change the delay between generations without restarting the simulation.
- Status: Displays messages:
- Statistics:
Generation: Number of the current simulation step.Living cells: Number of living cells (algorithm correctness test).Elapsed: Time of the last generation calculation in milliseconds.
Useful Links: #
- Microsoft Learn: Directory.EnumerateFiles Method
- Microsoft Learn: CancellationTokenSource.IsCancellationRequested Property
- Microsoft Learn: File.ReadAllLinesAsync Method
- Microsoft Learn: Task cancellation
- Microsoft Learn: Parallel.For Method
- Microsoft Learn: Interlocked.Add
- Microsoft Learn: Stopwatch Class
- Microsoft Learn: Progress
Class - Microsoft Learn: Task.Run Method
- Microsoft Learn: TaskCanceledException Class
Stage 1: Asynchronous Loading of Examples #
The application must load boards from files without “freezing” the user interface.
Open the file Services/FileService.cs.
- Method
EnumerateFilesAsync: (2 pts.)- Return paths to text files located directly in the
folderPathfolder asIAsyncEnumerable<string>. - Use
Directory.EnumerateFilesto get a list of.txtfiles. - Handle
CancellationToken– if cancellation is requested, break the loop. - Before returning each path, add a delay of
100milliseconds.
- Return paths to text files located directly in the
- Method
LoadBoardAsync: (1 pt.)- Asynchronously load all lines from the file
filePath. - Return a filled two-dimensional array
bool[,]of sizerows × cols. - Character
0in a content line corresponds to a living cell (valuetruein the array). Character\nstarts a new row. - Any other character corresponds to a dead cell (value
falsein the array). - Note: The file may contain fewer or more lines/characters per line than
rowsorcols. Fill the array starting from the top-left corner. We assume that all remaining cells are dead.
- Asynchronously load all lines from the file
Exemplary contents of the input file:
.....
...0.
.0.0.
..00.
.....Testing: #
Click the Choose directory... button and select a folder with examples.
The list on the left side of the window should fill with the contents of the selected folder.
Stage 2: Game Engine and Parallelism #
Your task is to calculate the state of the board in the next generation.
The game takes place on a two-dimensional board consisting of cells that can be living (true) or dead (false).
Open the file Models/LifeEngine.cs.
- Method
CalculateNextGeneration: (3 pts.)- Return a
SimulationStepResultobject containing information about the total number of living cells and the time in which the new state was calculated. - The state of a cell in the next turn depends on the number of its living neighbors, which is returned by the ready-made function
int CountLiveNeighbors(int y, int x). - Apply the game rules:
- Survival: A living cell with 2 or 3 neighbors stays alive.
- Birth: A dead cell with exactly 3 neighbors becomes alive.
- Death: In any other case, the cell becomes/remains dead.
- Use
Parallel.Forto compute board rows in parallel. - Hint: Remember that during calculations, you cannot modify the
Gridarray (current state) from which you are reading data. - Hint: To sum the total number of living cells in a parallel loop, use
Interlocked.Add. - Hint: Use the
Parallel.Forloop andParallelOptionsto pass thetokenparameter to the loop.
- Return a
Stage 3: Connecting UI to Logic #
This is the most important stage where you will connect the UI to the logic, ensuring that the application does not “hang” and responds to changes.
Open the file ViewModels/MainWindowViewModel.cs. It contains a partial class definition that you will be extending.
The remaining part (closely related to the Avalonia graphical framework used) is in ViewModels/UI/MainWindowViewModel.UI.cs (you don’t need to edit it).
All variables written in special font are either function parameters or exist in the file ViewModels/UI/MainWindowViewModel.UI.cs and you don’t need to declare them.
- Method
SimulationLoop: (2 pts.)- Calculates the new state of the game in a loop.
- In each iteration:
- Reports progress using the
progressargument. An object of typeSimulationFrameshould contain a cloned (.Clone()) internal array of the engine_engine. - Delays its execution by
SimulationDelaymilliseconds.
- Reports progress using the
- Ends execution depending on the state of the
tokenparameter.
- Method
StartSimulationFromFileAsync: (4 pts.)- Stops execution of the previous simulation (use
_simulationCancellationTokenSource). - If the previous simulation was in progress,
awaitthe_currentSimulationTask. - Loads the board contents from the file
filePathusing_fileService. Its size isBoardSize × BoardSize. - Sets the state of the game engine
_engine(usingLoadStatemethod ofLifeEngineclass) and initial values for the status bar (statistics:Generation,StatusText). - Runs the
SimulationLoopmethod without waiting (withoutawait) usingTask.Run. TheTaskobject returned by the method should be assigned to_currentSimulationTask. - Defines the progress reporting logic (creates an object of type
Progress<SimulationFrame>). - Progress updates:
- The number of living cells
LiveCells. - The last calculation time
LastCalculationTimeMs. - The generation
Generation - Refreshes the board by assigning the new board state to the variable
CurrentGrid.
- The number of living cells
- Stops execution of the previous simulation (use
Testing Everything: #
- Click
Choose directory...– files should appear on the list one by one (thanks toTask.Delayin the service). - Click a file on the list – the simulation should start.
- Change the speed slider – the simulation should speed up/slow down immediately.
Example solution #
- GameOfLife/
- .gitattributes
- .gitignore
- GameOfLife.UI/
- App.axaml
- App.axaml.cs
- Assets/
- Controls/
- Converters/
- Examples/
- GameOfLife.UI.csproj
- Models/
- Program.cs
- Services/
- ViewLocator.cs
- ViewModels/
- Views/
- app.manifest
- GameOfLife.sln