ladybird/Userland/Games/Conway/Game.cpp
Brian Gianforcaro 1682f0b760 Everything: Move to SPDX license identifiers in all files.
SPDX License Identifiers are a more compact / standardized
way of representing file license information.

See: https://spdx.dev/resources/use/#identifiers

This was done with the `ambr` search and replace tool.

 ambr --no-parent-ignore --key-from-file --rep-from-file key.txt rep.txt *
2021-04-22 11:22:27 +02:00

163 lines
3.9 KiB
C++

/*
* Copyright (c) 2021, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Game.h"
#include <LibGUI/Painter.h>
#include <stdlib.h>
#include <time.h>
Game::Game()
{
reset();
}
Game::~Game()
{
}
void Game::reset()
{
stop_timer();
seed_universe();
start_timer(m_sleep);
update();
}
void Game::seed_universe()
{
for (int y = 0; y < m_rows; y++) {
for (int x = 0; x < m_columns; x++) {
m_universe[y][x] = (arc4random() % 2) ? 1 : 0;
}
}
}
void Game::update_universe()
{
bool new_universe[m_rows][m_columns];
for (int y = 0; y < m_rows; y++) {
for (int x = 0; x < m_columns; x++) {
int n = 0;
auto cell = m_universe[y][x];
for (int y1 = y - 1; y1 <= y + 1; y1++) {
for (int x1 = x - 1; x1 <= x + 1; x1++) {
if (m_universe[(y1 + m_rows) % m_rows][(x1 + m_columns) % m_columns]) {
n++;
}
}
}
if (cell)
n--;
if (n == 3 || (n == 2 && cell))
new_universe[y][x] = true;
else
new_universe[y][x] = false;
}
}
for (int y = 0; y < m_rows; y++) {
for (int x = 0; x < m_columns; x++) {
m_universe[y][x] = new_universe[y][x];
}
}
}
void Game::timer_event(Core::TimerEvent&)
{
update_universe();
update();
}
Gfx::IntRect Game::first_cell_rect() const
{
auto game_rect = rect();
auto cell_size = Gfx::IntSize(game_rect.width() / m_columns, game_rect.height() / m_rows);
auto x_margin = (game_rect.width() - (cell_size.width() * m_columns)) / 2;
auto y_margin = (game_rect.height() - (cell_size.height() * m_rows)) / 2;
return { x_margin, y_margin, cell_size.width(), cell_size.height() };
}
void Game::paint_event(GUI::PaintEvent& event)
{
GUI::Painter painter(*this);
painter.add_clip_rect(event.rect());
painter.fill_rect(event.rect(), m_dead_color);
auto first_rect = first_cell_rect();
for (int y = 0; y < m_rows; y++) {
for (int x = 0; x < m_columns; x++) {
Gfx::IntRect rect {
x * first_rect.width() + first_rect.left(),
y * first_rect.height() + first_rect.top(),
first_rect.width(),
first_rect.height()
};
painter.fill_rect(rect, m_universe[y][x] ? m_alive_color : m_dead_color);
}
}
}
void Game::mousedown_event(GUI::MouseEvent& event)
{
switch (event.button()) {
case GUI::MouseButton::Left:
case GUI::MouseButton::Right:
m_last_button = event.button();
break;
default:
return;
}
interact_at(event.position());
}
void Game::mouseup_event(GUI::MouseEvent& event)
{
if (event.button() == m_last_button)
m_last_button = GUI::MouseButton::None;
}
void Game::mousemove_event(GUI::MouseEvent& event)
{
interact_at(event.position());
}
void Game::interact_at(const Gfx::IntPoint& point)
{
if (m_last_button == GUI::MouseButton::None)
return;
auto first_rect = first_cell_rect();
// Too tiny window, we don't actually display anything.
if (first_rect.width() == 0 || first_rect.height() == 0)
return;
// Too far left/up.
if (point.x() < first_rect.left() || point.y() < first_rect.top())
return;
int cell_x = (point.x() - first_rect.left()) / first_rect.width();
int cell_y = (point.y() - first_rect.top()) / first_rect.height();
// Too far right/down.
if (cell_x >= m_columns || cell_y >= m_rows)
return;
switch (m_last_button) {
case GUI::MouseButton::Left:
m_universe[cell_y][cell_x] = true;
break;
case GUI::MouseButton::Right:
m_universe[cell_y][cell_x] = false;
break;
default:
VERIFY_NOT_REACHED();
}
}