Question
Objective in C++ Make a struct in order to store settings for the application Instructions In order to allow the Game of Life to be
Objective in C++
Make a struct in order to store settings for the application
Instructions
- In order to allow the Game of Life to be customized, it will be necessary to keep track of a few different settings. A couple of these settings already have values that are being tracked in the program. Creating an object for the settings will make it easier to pass around the program from object to object.
- Make a struct that for the game settings in a new header file. No need for a .cpp file in this case. At a minimum, the struct will need to track the following pieces of data for now.
- The color of living cells, stored as unsigned ints representing the Red, Blue, Green, and Alpha values. Default the RGB values to 128 and the Alpha to 255.
- The color of dead cells, stored as unsigned ints representing the Red, Blue, Green, and Alpha values. Default the RGB values to 255 and the Alpha to 255.
- It is possible to run into Read Access errors if trying to read a wxColor that has been loaded from file. This is because one of the classes the wxColor inherits from does not like being written to directly. In order to get around this error, don't store wxColor directly. Instead store the Red, Green, Blue, and Alpha values as unsigned ints. wxColor has .GetRed(), .GetGreen(), .GetBlue(), and .GetAlpha() methods that can be used. It may make sense to create a method in the Settings struct that returns a wxColor based on the specific RGBA values that are stored.
- The grid size (Default to 15)
- The interval (Default to 50)
- Eventually information about the window size and location may be added to this struct, as well as other any other options that can be tracked.
- It may make sense to add methods for getting the living cell color and getting the dead cell color that returns a wxColor. Creating setters for living cell color and dead cell color would make sense too. Those could take in a wxColor and set the RGBA values from those colors.
- Add a variable to the main window header for the settings object. This should not be a pointer.
- Since the grid size will now be tracked in the settings object, it will be necessary to remove all existing references to that variable and replace them with the reference to the settings object.
- The easiest way to do this is to removethe size variable from the main window header file.
- The .cpp file will have many errors at this point. Each error can be fixed by referencing the appropriate variable in the settings file.
- Now that there is a settings object, this can be used to pass data to the drawing panel. Make a settings pointer in the drawing panel header file.
- The value of the settings object pointer can either be set in the drawing panel constructor or through a setter. The easiest way is likely to create a setter for the settings pointer.
- It will again be necessary to remove all existing references to variables in the drawing panel and replace them with the reference to the settings object pointer.
- To test, change the default values in the settings object file and open the program. The interval, grid size, and colors should change based on the default values.
wxBEGIN_EVENT_TABLE(DrawingPanel, wxPanel)
EVT_PAINT(DrawingPanel::OnPaint)
EVT_LEFT_UP(DrawingPanel::OnMouseUp)
wxEND_EVENT_TABLE()
DrawingPanel::DrawingPanel(wxFrame* parent, std::vector
{
this->SetBackgroundStyle(wxBG_STYLE_PAINT);
gridSize = 30;
CalculateCellSize();
}
DrawingPanel::~DrawingPanel()
{
}
void DrawingPanel::CalculateCellSize()
{
wxSize panelSize = GetParent()->GetClientSize();
cellWidth = panelSize.GetWidth() / gridSize;
cellHeight = panelSize.GetHeight() / gridSize;
}
void DrawingPanel::OnPaint(wxPaintEvent& event)
{
wxAutoBufferedPaintDC dc(this);
dc.Clear();
wxGraphicsContext* context = wxGraphicsContext::Create(dc);
if (!context)
{
return;
}
context->SetPen(*wxBLACK);
context->SetBrush(*wxWHITE);
for (int row = 0; row < gridSize; row++)
{
for (int col = 0; col < gridSize; col++)
{
int x = col * cellWidth;
int y = row * cellHeight;
// Checks the corresponding bool in the game board
if (_gameBoard[row][col])
{
context->SetBrush(*wxLIGHT_GREY); // Living cell color
}
else
{
context->SetBrush(*wxWHITE); // Dead cell color
}
context->DrawRectangle(x, y, cellWidth, cellHeight);
}
}
}
void DrawingPanel::SetGridSize(int size)
{
gridSize = size;
CalculateCellSize(); // Recalculates cell size based on the new grid size
}
void DrawingPanel::SetSize(const wxSize& size)
{
// Call SetSize on base wxPanel class
wxPanel::SetSize(size);
// Call Refresh
Refresh();
}
void DrawingPanel::OnMouseUp(wxMouseEvent& event)
{
int mouseX = event.GetX();
int mouseY = event.GetY();
int row = mouseY / cellHeight;
int col = mouseX / cellWidth;
if (row >= 0 && row < gridSize && col >= 0 && col < gridSize)
{
// Toggles the state of the clicked cell
_gameBoard[row][col] = !_gameBoard[row][col];
Refresh();
}
}
wxBEGIN_EVENT_TABLE(MainWindow, wxFrame)
EVT_SIZE(MainWindow::OnSizeChanged)
EVT_TOOL(TOOLBAR_PLAY_ICON, MainWindow::OnPlay)
EVT_TOOL(TOOLBAR_PAUSE_ICON, MainWindow::OnPause)
EVT_TOOL(TOOLBAR_NEXT_ICON, MainWindow::OnNext)
EVT_TOOL(TOOLBAR_TRASH_ICON, MainWindow::OnTrash)
EVT_TIMER(wxID_ANY, MainWindow::OnTimer)
wxEND_EVENT_TABLE();
MainWindow::MainWindow() :wxFrame(nullptr, wxID_ANY, "Game of Life", wxPoint(0, 0), wxSize(200, 200))
{
_drawingPanel = new DrawingPanel(this, _gameBoard);
_sizer = new wxBoxSizer(wxVERTICAL);
_toolbar = CreateToolBar();
this->Bind(wxEVT_SIZE, &MainWindow::OnSizeChanged, this);
// Adds Play button to the toolbar
wxBitmap playIcon(play_xpm);
_toolbar->AddTool(TOOLBAR_PLAY_ICON, "Play", playIcon);
// Adds Pause button to the toolbar
wxBitmap pauseIcon(pause_xpm);
_toolbar->AddTool(TOOLBAR_PAUSE_ICON, "Pause", pauseIcon);
// Adds Next button to the toolbar
wxBitmap nextIcon(next_xpm);
_toolbar->AddTool(TOOLBAR_NEXT_ICON, "Next", nextIcon);
// Adds Trash button to the toolbar
wxBitmap trashIcon(trash_xpm);
_toolbar->AddTool(TOOLBAR_PLAY_ICON, "Trash", trashIcon);
gameTimer = new wxTimer(this);
timerInterval = 50;
_toolbar->Realize();
_sizer->Add(_drawingPanel, 1, wxEXPAND | wxALL);
// Initializes the status bar
statusBar = CreateStatusBar();
SetSizer(_sizer);
InitializeGameBoard();
this->Layout();
}
MainWindow::~MainWindow()
{
gameTimer->Stop(); // Stop the timer before destroying the frame
delete gameTimer;
}
void MainWindow::OnSizeChanged(wxSizeEvent& event)
{
// Save the size of the window
wxSize windowSize = event.GetSize();
// Pass the size to DrawingPanel
_drawingPanel->SetSize(windowSize);
// Stop other events from being processed
event.Skip();
}
void MainWindow::InitializeGameBoard()
{
_gameBoard.resize(gridSize); // Resizes the game board vector to the grid size
for (int i = 0; i < gridSize; i++)
{
_gameBoard[i].resize(gridSize); // Resizes each sub-vector to the grid size
}
_drawingPanel->SetGridSize(gridSize); // Passes the grid size to the drawing panel
}
void MainWindow::UpdateStatusBar()
{
wxString statusText = wxString::Format("Generation: %d Living Cells: %d", generationCount, livingCellsCount);
statusBar->SetStatusText(statusText, 0);
}
// Event handler for the Play button
void MainWindow::OnPlay(wxCommandEvent& event)
{
gameTimer->Start(timerInterval);
}
// Event handler for the Pause button
void MainWindow::OnPause(wxCommandEvent& event)
{
gameTimer->Stop();
}
// Event handler for the Next button
void MainWindow::OnNext(wxCommandEvent& event)
{
CalculateNextGeneration();
}
// Event handler for the Trash button
void MainWindow::OnTrash(wxCommandEvent& event)
{
for (int row = 0; row < gridSize; ++row)
{
for (int col = 0; col < gridSize; ++col)
{
_gameBoard[row][col] = false;
}
}
generationCount = 0;
livingCellsCount = 0;
UpdateStatusBar();
_drawingPanel->Refresh();
}
void MainWindow::OnTimer(wxTimerEvent& event)
{
CalculateNextGeneration();
}
int MainWindow::GetNeighborCount(int row, int col)
{
int count = 0;
for (int r = row - 1; r <= row + 1; r++)
{
for (int c = col - 1; c <= col + 1; c++)
{
// Makes sure index is valid
if (r >= 0 && r < gridSize && c >= 0 && c < gridSize)
{
if (!(r == row && c == col))
{
if (_gameBoard[r][c])
{
count++;
}
}
}
}
}
return count;
}
void MainWindow::CalculateNextGeneration()
{
// Sandbox with the same data type as the game board
std::vector
// Iterates through the game board to calculate the next generation
int newLivingCellsCount = 0;
for (int row = 0; row < gridSize; ++row)
{
for (int col = 0; col < gridSize; ++col)
{
// Counts the number of living neighbors for the current cell
int neighborCount = GetNeighborCount(row, col);
if (_gameBoard[row][col])
{
if (neighborCount < 2 || neighborCount > 3)
{
// Cell dies in the next generation
sandbox[row][col] = false;
}
else
{
// Cell survives to the next generation
sandbox[row][col] = true;
++newLivingCellsCount;
}
}
else
{
if (neighborCount == 3)
{
// Dead cell becomes alive in the next generation
sandbox[row][col] = true;
++newLivingCellsCount;
}
}
}
}
// Updates the game board with the new generation using swap
_gameBoard.swap(sandbox);
// Updates counts and status bar
++generationCount;
livingCellsCount = newLivingCellsCount;
UpdateStatusBar();
_drawingPanel->Refresh();
}
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started