kde-extraapps/kdeplasma-addons/wallpapers/virus/alife.cpp
Ivailo Monev 7c098060fd generic: make changes required for building against Katie
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2015-11-25 11:50:42 +00:00

626 lines
18 KiB
C++

/*
Copyright (c) 2009 by Beat Wolf <asraniel@fryx.ch>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
#include "alife.h"
#include <QtCore/qdatetime.h>
#include <KDebug>
#define VIRUS_GENOME_SIZE 38
#define MAX_AGE 8
#define MAX_EAT 10
#define MIN_EAT 1
Alife::Alife(){
m_cells = 0;
m_height = 0;
m_width = 0;
m_max_attended = false;
m_current_eat = MAX_EAT;
m_current_eat_best = MIN_EAT;
}
Alife::~Alife(){
resetLife();
}
void Alife::resetLife(){
mutex.lock();
if(inited()){
while( !m_livingCells.isEmpty() ){
struct cell* cell = m_livingCells.takeFirst();
delete [] cell->code;
}
delete []m_cells[0];
delete []m_cells;
m_cells = 0;
}
mutex.unlock();
//qDebug() << "life reseted";
}
void Alife::setImage(QImage image){
bool keepViruses = false;
m_image = image;
m_image_original = image;
keepViruses = m_height == m_image.height() && m_width == m_image.width();
m_height = m_image.height();
m_width = m_image.width();
m_max_attended = false;
if(!keepViruses || !inited()){
resetLife();
initVirus();
}
}
void Alife::initVirus(){
//magic 2d array code
if(!m_cells) {
m_cells = new struct cell*[m_width];
m_cells[0] = new struct cell[m_width * m_height];
for(int i = 1; i < m_width; ++i) {
m_cells[i] = m_cells[i-1] + m_height;
}
}
m_livingCells.clear();
m_startViruses = 20;
for(int x = 0; x < m_width; x++) {
for(int y = 0; y < m_height; y++) {
struct cell *temp = &m_cells[x][y];
resetCell(temp);
temp->x = x;
temp->y = y;
}
}
createViruses(m_startViruses);
//qDebug() << "life created";
}
//create the needed viruses so that we have in total "amount" viruses
void Alife::createViruses(int amount){
for(int i = m_livingCells.size(); i < amount; i++) {
int x = rand() % m_width;
int y = rand() % m_height;
struct cell *temp = &m_cells[x][y];
//cell already alive
if(temp->alive) {
i--;
} else {
temp->alive = true;
temp->energy = 255;
temp->code = new uchar[VIRUS_GENOME_SIZE];
memset(temp->code, 0, VIRUS_GENOME_SIZE);
//initial code
for(int i = 0; i < 7; i++) {
temp->code[i] = rand()%12;
}
temp->code[rand()%7] = 7; //cheating, jumpstart evolution
/*temp->code[0] = 4;
temp->code[1] = 12;
temp->code[2] = 2;
temp->code[3] = 7;*/
/*temp->code[0] = 1;
temp->code[1] = 10;
temp->code[2] = 19;
temp->code[3] = 4;
temp->code[4] = 19;
temp->code[5] = 6;
temp->code[6] = 19;
temp->code[7] = 6;
temp->code[8] = 12;
temp->code[9] = 2;
temp->code[10] = 7;
temp->code[11] = 13;
temp->code[12] = 2;
temp->code[13] = 3;
temp->code[14] = 9;
temp->code[15] = 11;
temp->code[16] = 7;*/
m_livingCells.append(temp);
}
}
}
//resets the values of a cell
void Alife::resetCell(struct cell *temp)
{
temp->alive = false;
temp->energy = 0;
temp->code = 0;
temp->age = 0;
temp->killMe = false;
temp->r = 0;
temp->g = 0;
temp->b = 0;
}
//returns a random operation for the genome.
//REMINDER: Update if a new op is added
uchar Alife::randomCode()
{
return rand() % 20;
}
//Executes the code of a certain living cell
void Alife::executeCell(int id)
{
struct cell* cell = m_livingCells.at(id);
//kDebug() << cell->energy;
if(cell->killMe)
return;
cell->age++;
int facing = 0; //looking in direction
int reg = 0;
int codePointer = 0;
int better_eat = m_current_eat; //what inverse percentage is eaten next time
bool stop = false;
int max = 300;
int special = 4;
QRgb pixel = m_image.pixel(cell->x, cell->y); //current pixel
QRgb pixelBackup = pixel;
while(!stop && cell->energy > 0 && max) {
cell->energy--;
switch(cell->code[codePointer]){
case 0: //stop
stop = true;
break;
case 1://random value
reg = rand() % 4;
break;
case 2: //change direction
facing = reg & 3;
break;
case 3: //move
if(moveCell(id, facing)){
if(pixelBackup != pixel) {
m_image.setPixel(cell->x, cell->y, pixel );
updateAffectedArea(cell->x, cell->y);
}
cell = m_livingCells.at(id);
pixel = m_image.pixel(cell->x, cell->y);
pixelBackup = pixel;
special--;
if(special <= 0) {
stop = true;
}
}
break;
case 4:{ //eat red
int color = qRed(pixel);
if(color && cell->energy + color < 255) {
int temp = qMin(color / better_eat, color);
color -= temp;
cell->energy += temp;
better_eat = m_current_eat;
}
pixel = qRgb(color, qGreen(pixel), qBlue(pixel));
}break;
case 5:{ //eat green
int color = qGreen(pixel);
if(color && cell->energy + color < 255) {
int temp = qMin(color / better_eat, color);
color -= temp;
cell->energy += temp;
better_eat = m_current_eat;
}
pixel = qRgb(qRed(pixel), color, qBlue(pixel));
}break;
case 6:{ //eat blue
int color = qBlue(pixel);
if(color && cell->energy + color < 255) {
int temp = qMin(color / better_eat, color);
color -= temp;
cell->energy += temp;
better_eat = m_current_eat;
}
pixel = qRgb(qRed(pixel), qGreen(pixel), color);
}break;
case 7: //reproduce
reproduce(cell, facing, pixel);
special-= 2;
if(special <= 0) {
stop = true;
}
break;
case 8: //increment register
reg++;
if(reg > 255) {
reg = 0;
}
break;
case 9: //decrement register
reg--;
if(reg < 0) {
reg = 255;
}
break;
case 10: //while(register){
if(!reg){
while(codePointer < VIRUS_GENOME_SIZE-1 && cell->code[codePointer] != 11 && cell->energy){
codePointer++;
cell->energy--;
}
}
break;
case 11: //}
if(reg){
while(codePointer >= 0 && cell->code[codePointer] != 10 && cell->energy){
codePointer--;
cell->energy--;
}
}
break;
case 12:{ //highest red
int best = 0;
int bestColor = 0;
for(int i = 0; i < 4; i++){
QPoint neighbour = getNeighbour(cell->x, cell->y, i);
QRgb pixel = m_image.pixel(neighbour.x(),neighbour.y());
int color = qRed(pixel);
if(color > bestColor){
bestColor = color;
best = i;
}
}
reg = best;
}break;
case 13:{ //highest green
int best = 0;
int bestColor = 0;
for(int i = 0; i < 4; i++){
QPoint neighbour = getNeighbour(cell->x, cell->y, i);
QRgb pixel = m_image.pixel(neighbour.x(),neighbour.y());
int color = qGreen(pixel);
if(color > bestColor){
bestColor = color;
best = i;
}
}
reg = best;
}break;
case 14:{ //highest blue
int best = 0;
int bestColor = 0;
for(int i = 0; i < 4; i++){
QPoint neighbour = getNeighbour(cell->x, cell->y, i);
QRgb pixel = m_image.pixel(neighbour.x(),neighbour.y());
int color = qBlue(pixel);
if(color > bestColor){
bestColor = color;
best = i;
}
}
reg = best;
}break;
case 15:{ //empty neighbour
int best = 0;
for(int i = 0; i < 4; i++){
QPoint neighbour = getNeighbour(cell->x, cell->y, i);
struct cell *temp = &m_cells[neighbour.x()][neighbour.y()];
if(!temp->alive){
best = i;
}
}
reg = best;
}break;
case 16: // NOP
break;
case 17:{ //kill facing
QPoint neighbour = getNeighbour(cell->x, cell->y, facing);
struct cell *temp = &m_cells[neighbour.x()][neighbour.y()];
if(temp->alive && temp->code[0] == reg) {
temp->killMe = true;
int enTemp = cell->energy + temp->energy / 2;
cell->energy = qMin(255, enTemp);
}
}break;
case 18:{ //give
QPoint neighbour = getNeighbour(cell->x, cell->y, facing);
struct cell *temp = &m_cells[neighbour.x()][neighbour.y()];
int enTemp = temp->energy + cell->energy / 2;
temp->energy = qMin(255, enTemp);
cell->energy /= 2;
}break;
case 19:{ //better eat
better_eat = m_current_eat_best;
}break;
default:
kDebug() << "wah" << cell->code[codePointer] << codePointer;
break;
}
codePointer++;
if(codePointer >= VIRUS_GENOME_SIZE) {
stop = true;
}
better_eat = qMin(better_eat + 1, m_current_eat);
max--;
}
if(pixelBackup != pixel) {
m_image.setPixel(cell->x, cell->y, pixel );
updateAffectedArea(cell->x, cell->y);
}
if(cell->energy <= 0) {
cell->energy = 0;
cell->killMe = true;
}
}
bool Alife::reproduce(struct cell* cell, int direction, QRgb color)
{
QPoint neighbour = getNeighbour(cell->x, cell->y, direction);
struct cell* newCell = &m_cells[neighbour.x()][neighbour.y()];
if(!newCell->alive&& m_livingCells.size() < m_maxViruses) {
//give a unfair advantage to darker places
int prob = (((qRed(color) + qGreen(color) + qBlue(color))/ 255.)+1);// 0 - 765
if(rand() % prob != 0){
return false;
}
resetCell(newCell);
newCell->alive = true;
newCell->code = new uchar[VIRUS_GENOME_SIZE];
memset(newCell->code, 0, VIRUS_GENOME_SIZE);
newCell->energy = cell->energy / 3;
cell->energy = cell->energy / 3;
memcpy(newCell->code,cell->code,VIRUS_GENOME_SIZE);
int mutate = rand() % 3;
if(mutate) {
//normal mutation
int mutations = rand() % 5;
for(int i = 0; i < mutations; i++) {
int index = rand() % VIRUS_GENOME_SIZE;
newCell->code[index] = randomCode();
}
int duplication = rand() % 3;
for(int i = 0; i < duplication; i++) {
int start = rand() % VIRUS_GENOME_SIZE;
int end = start + rand() % ( VIRUS_GENOME_SIZE - start);
memcpy(&newCell->code[end],&cell->code[start],VIRUS_GENOME_SIZE - end);
}
int deletion = rand() % 3;
for(int i = 0; i < deletion; i++) {
int start = rand() % VIRUS_GENOME_SIZE;
int end = start + rand() % ( VIRUS_GENOME_SIZE - start);
memcpy(&newCell->code[start],&cell->code[end],VIRUS_GENOME_SIZE - end);
memset(&newCell->code[end], 0, VIRUS_GENOME_SIZE - end);
}
}
int r = 0;
int g = 0;
int b = 0;
int mod = 1;
for(int i = 0; i < VIRUS_GENOME_SIZE; i++) {
switch(newCell->code[i]) {
case 4:
r += 40 * mod;
break;
case 5:
g += 40 * mod;
break;
case 6:
b += 40 * mod;
break;
case 10:
mod = 2;
break;
case 11:
mod = 1;
break;
case 12:
r += 20 * mod;
break;
case 13:
g += 20 * mod;
break;
case 14:
b += 20 * mod;
break;
}
}
newCell->r = qMin(r, 255);
newCell->g = qMin(g, 255);
newCell->b = qMin(b, 255);
m_livingCells.append(newCell);
return true;
}
return false;
}
bool Alife::moveCell(int i, int direction)
{
struct cell* cell = m_livingCells.at(i);
QPoint neighbour = getNeighbour(cell->x, cell->y, direction);
struct cell* newCell = &m_cells[neighbour.x()][neighbour.y()];
if(!newCell->alive) {
newCell->code = cell->code;
newCell->alive = true;
newCell->energy += cell->energy / 2;
newCell->age = cell->age;
newCell->r = cell->r;
newCell->g = cell->g;
newCell->b = cell->b;
resetCell(cell);
cell->energy = 0;
m_livingCells[i] = newCell;
return true;
}
return false;
}
//performance critical
int Alife::normalXY(int coord, int max)
{
if(coord < 0) {
return max;
}
if(coord > max) {
return 0;
}
return coord;
}
QPoint Alife::getNeighbour(int x, int y, int direction)
{
if(direction == 0 || direction == 2) {
y--;
y = normalXY(y + direction, m_height - 1);
return QPoint(x,y);
}
x-=2;
x = normalXY(x + direction, m_width -1);
return QPoint(x,y);
}
void Alife::run(){
mutex.lock();
QTime a = QTime::currentTime();
qsrand(a.msec());
virusMove();
//QTime b = QTime::currentTime();
//qDebug() << "needed" << a.msecsTo(b);
mutex.unlock();
}
//performance critical
void Alife::virusMove()
{
m_tl = QPoint(m_width, m_height);
m_br = QPoint(0,0);
m_current_eat = qMax(MAX_EAT, (int)(((double)m_livingCells.size()/ (double) (m_maxViruses / 4.0)) * MAX_EAT));
m_current_eat_best = qMax(MIN_EAT, (int)(((double)m_livingCells.size()/ (double) (m_maxViruses / 4.0)) * (MIN_EAT * 2)));
bool reseted = false;
//kDebug() << m_current_eat_best << m_current_eat << m_livingCells.size() << m_maxViruses;
if(m_livingCells.size() < m_startViruses / 3) {
createViruses(m_startViruses);
reseted = true;
}
if(!m_max_attended && m_livingCells.size() > m_maxViruses / 10) {
m_max_attended = true;
}
if(m_max_attended && m_livingCells.size() < 4 * m_startViruses) {
m_image = m_image_original;
m_max_attended = false;
}
/*struct cell* myCell = m_livingCells.at(0);
int pointer = 0;
kDebug() << "start code";
while(myCell->code[pointer] != 0 && pointer < VIRUS_GENOME_SIZE) {
kDebug() << myCell->code[pointer++];
}file:///home/kde-devel/kde/src/kdeplasma-addons/wallpapers/virus/alife.h
kDebug() << "end code";*/
int cells = m_livingCells.size();
for(int i = 0; i < cells; i++) {
executeCell(i);
}
//kill old cells
for(int i = m_livingCells.size() - 1; i >= 0; i--) {
struct cell* cell = m_livingCells.at(i);
if(cell->age > MAX_AGE || cell->killMe) {
if(cell->alive)
delete [] cell->code;
resetCell(cell);
m_livingCells.removeAt(i);
}
}
/*QImage newImage = m_image;
for(int x = 0; x < m_width; x++) {
for(int y = 0; y < m_height; y++) {
struct cell *cell = &m_cells[x][y];
newImage.setPixel(cell->x,cell->y,qRgb(cell->energy,cell->energy,cell->energy));file:///home/kde-devel/kde/src/kdeplasma-addons/wallpapers/virus/virus.cpp
}
}file:///home/kde-devel/kde/src/kdeplasma-addons/wallpapers/virus/virus.h
m_pixmap = QPixmap::fromImage(newImage);*/
if(m_showCells) {
//add virus overlay
QImage newImage = m_image;
int size = m_livingCells.size();
for(int i = 0; i < size; i++) {
struct cell* cell = m_livingCells.at(i);
newImage.setPixel(cell->x,cell->y,qRgb(cell->r,cell->g,cell->b));
if(!reseted){
updateAffectedArea(cell->x,cell->y);
}
}
m_current = newImage;
} else {
//TODO: also only update affected area
m_current = m_image;
}
}
void Alife::updateAffectedArea(int x, int y)
{
if (m_tl.x() > x) {
m_tl.setX(x);
}else if (m_br.x() < x) {
m_br.setX(x);
}
if (m_tl.y() > y) {
m_tl.setY(y);
} else if(m_br.y() < y) {
m_br.setY(y);
}
}
QRect Alife::updatedArea(){
return QRect(m_tl, m_br);
}