mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-24 10:52:53 +00:00
632 lines
18 KiB
C++
632 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 <QDateTime>
|
|
#include <KDebug>
|
|
#include <KRandom>
|
|
|
|
#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 = KRandom::randomMax(m_width);
|
|
int y = KRandom::randomMax(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] = KRandom::randomMax(12);
|
|
}
|
|
|
|
temp->code[KRandom::randomMax(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 KRandom::randomMax(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 = KRandom::randomMax(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(KRandom::randomMax(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 = KRandom::randomMax(3);
|
|
if(mutate) {
|
|
//normal mutation
|
|
int mutations = KRandom::randomMax(5);
|
|
for(int i = 0; i < mutations; i++) {
|
|
int index = KRandom::randomMax(VIRUS_GENOME_SIZE);
|
|
newCell->code[index] = randomCode();
|
|
}
|
|
|
|
int duplication = KRandom::randomMax(3);
|
|
for(int i = 0; i < duplication; i++) {
|
|
int start = KRandom::randomMax(VIRUS_GENOME_SIZE);
|
|
int end = start + KRandom::randomMax( VIRUS_GENOME_SIZE - start);
|
|
memcpy(&newCell->code[end],&cell->code[start],VIRUS_GENOME_SIZE - end);
|
|
}
|
|
|
|
int deletion = KRandom::randomMax(3);
|
|
for(int i = 0; i < deletion; i++) {
|
|
int start = KRandom::randomMax(VIRUS_GENOME_SIZE);
|
|
int end = start + KRandom::randomMax( 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();
|
|
|
|
virusMove();
|
|
|
|
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);
|
|
}
|