/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "gameoflifemodel.h"#include <QFile>#include <QTextStream>#include <QRect>
GameOfLifeModel::GameOfLifeModel(QObject*parent)
: QAbstractTableModel(parent)
{
clear();
}
int GameOfLifeModel::rowCount(constQModelIndex&parent) const
{
if (parent.isValid())
return0;
return height;
}
int GameOfLifeModel::columnCount(constQModelIndex&parent) const
{
if (parent.isValid())
return0;
return width;
}
QVariant GameOfLifeModel::data(constQModelIndex&index,int role) const
{
if (!index.isValid() || role != CellRole)
returnQVariant();
returnQVariant(m_currentState[cellIndex({index.column(), index.row()})]);
}
bool GameOfLifeModel::setData(constQModelIndex&index,constQVariant&value,int role)
{
if (role != CellRole || data(index, role) == value)
returnfalse;
m_currentState[cellIndex({index.column(), index.row()})]= value.toBool();
emit dataChanged(index, index, {role});
returntrue;
}
Qt::ItemFlags GameOfLifeModel::flags(constQModelIndex&index) const
{
if (!index.isValid())
returnQt::NoItemFlags;
returnQt::ItemIsEditable;
}
void GameOfLifeModel::nextStep()
{
StateContainer newValues;
for (std::size_t i =0; i < size; ++i) {
bool currentState = m_currentState[i];
int cellNeighborsCount =this->cellNeighborsCount(cellCoordinatesFromIndex(static_cast<int>(i)));
newValues[i]= currentState ==true? cellNeighborsCount ==2|| cellNeighborsCount ==3
: cellNeighborsCount ==3;
}
m_currentState = std::move(newValues);
emit dataChanged(index(0,0), index(height -1, width -1), {CellRole});
}
bool GameOfLifeModel::loadFile(constQString&fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
returnfalse;
QTextStream in(&file);
loadPattern(in.readAll());
returntrue;
}
void GameOfLifeModel::loadPattern(constQString&plainText)
{
clear();
QStringList rows = plainText.split("\n");
QSize patternSize(0, rows.count());
for (QString row : rows) {
if (row.size() > patternSize.width())
patternSize.setWidth(row.size());
}
QPoint patternLocation((width - patternSize.width()) /2, (height - patternSize.height()) /2);
for (int y =0; y < patternSize.height(); ++y) {
constQString line = rows[y];
for (int x =0; x < line.length(); ++x) {
QPoint cellPosition(x + patternLocation.x(), y + patternLocation.y());
m_currentState[cellIndex(cellPosition)]= line[x]=='O';
}
}
emit dataChanged(index(0,0), index(height -1, width -1), {CellRole});
}
void GameOfLifeModel::clear()
{
m_currentState.fill(false);
emit dataChanged(index(0,0), index(height -1, width -1), {CellRole});
}
int GameOfLifeModel::cellNeighborsCount(constQPoint&cellCoordinates) const
{
int count =0;
for (int x =-1; x <=1; ++x) {
for (int y =-1; y <=1; ++y) {
if (x ==0&& y ==0)
continue;
constQPoint neighborPosition { cellCoordinates.x() + x, cellCoordinates.y() + y };
if (!areCellCoordinatesValid(neighborPosition))
continue;
if (m_currentState[cellIndex(neighborPosition)])
++count;
if (count >3)
return count;
}
}
return count;
}
bool GameOfLifeModel::areCellCoordinatesValid(constQPoint&coordinates)
{
returnQRect(0,0, width, height).contains(coordinates);
}
QPoint GameOfLifeModel::cellCoordinatesFromIndex(int cellIndex)
{
return {cellIndex % width, cellIndex / width};
}
std::size_t GameOfLifeModel::cellIndex(constQPoint&coordinates)
{
return std::size_t(coordinates.y() * width + coordinates.x());
}