Soyez épidémiologiste!

13

Défi

Vous devez créer un modèle simple de la façon dont la maladie se propage autour d'un groupe de personnes.

Règles et exigences

Le modèle doit être un tableau 2D de 1 000 par 1 000, chaque élément étant une personne différente.

L'utilisateur doit saisir trois variables en utilisant argv: la probabilité de transmission (la probabilité que quelqu'un va infecter quelqu'un d'autre), le risque de mutation et le nombre de périodes pendant lesquelles la simulation doit s'exécuter.

Dans la première période ( t=0), quatre personnes doivent être choisies au hasard et infectées par la maladie.

Le comportement de la maladie est régi par les règles suivantes:

  • La maladie ne peut se déplacer que verticalement et horizontalement, se déplaçant vers la personne d'à côté.
  • L'infection dure 3 périodes chez chaque personne. Vous ne pouvez pas prendre en compte les immunodéficiences.
  • Après qu'une personne a été infectée trois fois, elle est immunisée et ne peut plus être infectée.
  • La maladie est sujette à mutation, ce qui rend les personnes auparavant immunisées vulnérables à cette nouvelle maladie mutée. La maladie mutée a exactement les mêmes traits et suit les mêmes règles que la maladie d'origine.
  • Si une mutation se produit, la maladie entière ne change pas, juste ce «paquet» particulier lors de la transmission.
  • Une fois qu'une personne a été infectée par un virus, elle ne peut plus être infectée tant que l'infection actuelle n'est pas passée.
  • Si une personne est infectée, elle est contagieuse du début de sa période d'infection jusqu'à sa fin.
  • Il n'y a pas de niveaux d'immunité - une personne est immunisée ou non.
  • Pour arrêter la surcharge de la mémoire, il existe une limite maximale de 800 mutations.

À la fin du nombre de périodes spécifié, vous devez générer les résultats.

Les résultats doivent être une grille de 1000 x 1000 montrant quelles personnes sont infectées et quelles personnes ne le sont pas. Cela peut être sorti sous forme de fichier texte, sous forme de fichier image ou de sortie graphique (où #FFFFFF est une personne en bonne santé et # 40FF00 est une personne infectée).

Veuillez inclure le nom de la langue et une commande pour l'exécuter dans votre réponse.

Gagnant

Le code le plus rapide à exécuter sur mon ordinateur gagne. Son temps sera mesuré avec le morceau de code Python suivant:

import time, os
start = time.time()
os.system(command)
end = time.time()
print(end-start)

Notez que lors de l'exécution de ce script, j'utiliserai les valeurs par défaut suivantes:

Probability of transmission = 1
Chance of mutation = 0.01
Number of periods = 1000
Beta Decay
la source
3
Vous souhaitez créer un fichier de 10 gigaoctets ?
Ypnypn
1
En ayant une limite de 4 Go, vous avez complètement supprimé l'option d'enregistrement de la sortie dans un fichier image ...
Optimizer
10
1000x1000 : C'est plus comme ça!
COTO
1
Dites également qu'il y a deux personnes côte à côte. Le premier virus de contrats V, le deuxième virus de contrats V'. La contraction prendra fin à la même période. Le virus peut-il Vinfecter la deuxième personne? (Ou une question plus en noir et blanc: est-il possible qu'une personne soit infectée immédiatement après sa guérison, de sorte qu'elle se retrouvera avec 6 périodes consécutives d'infection?)
juste
1
Un autre, deux virus indépendants peuvent-ils muter en un même virus? Disons que nous avons Ven personne A, et Vencore en personne B. Lorsqu'ils transmettent le virus, peuvent-ils tous deux muter vers la même mutation V'? Ou peut-être qu'ils devraient en fait muter vers la même souche virale? S'ils peuvent muter arbitrairement, quelle est la probabilité de mutation de deux virus vers la même souche virale?
moitié du

Réponses:

10

J'étais curieux de savoir à quoi cela ressemblerait alors j'ai fait ce hack rapide et sale en JavaScript: http://jsfiddle.net/andrewmaxwell/r8m54t9c/

// The probability that a healthy cell will be infected by an adjacent infected cell EACH FRAME.
var infectionProbability = 0.2

// The probability that the infection will mutate on transmission EACH FRAME.
var mutationProbability = 0.00001

// The maximum number of times a cell can be infected by the same infection.
var maxInfections = 3

// The number of frames a cell in infected before it becomes healthy again.
var infectionDuration = 3

// The width and heigh of the board
var size = 400

// The number of cells infected at the beginning.
var startingNum = 4

var imageData, // the visual representation of the board
    cells, // array of cells
    infectionCount // counter that is incremented whenever a mutation occurs

// Just some colors. The colors are re-used as the number of mutations increases.
var colors = [[255,0,0],[255,255,0],[0,255,0],[0,255,255],[0,0,255],[255,0,255],[128,0,0],[128,128,0],[0,128,0],[0,128,128],[0,0,128],[128,0,128],[255,128,128],[255,255,128],[128,255,128],[128,255,255],[128,128,255],[255,128,255]
]

// when a cell is infected, it isn't contagious until the next frame
function infect(person, infection){
    person.infect = true
    person.infectionCounts[infection] = (person.infectionCounts[infection] || 0) + 1
    person.currentInfection = infection
}

// when a mutation occurs, it is given a number and the counter is incremented
function mutation(){
    return infectionCount++
}

function reset(){

    cells = []
    infectionCount = 0
    imageData = T.createImageData(size, size)

    // initialize the cells, store them in a grid temporarily and an array for use in each frame
    var grid = []
    for (var i = 0; i < size; i++){
        grid[i] = []
        for (var j = 0; j < size; j++){
            cells.push(grid[i][j] = {
                infectionTime: 0, // how many frames until they are no longer infected, so 0 is healthy
                infectionCounts: [], // this stores how many times the cell has been infected by each mutation
                neighbors: [] // the neighboring cells
            })
        }
    }

    // store the neighbors of each cell, I just want to minimize the work done each frame
    var neighborCoords = [[0,-1],[1,0],[0,1],[-1,0]]
    for (var i = 0; i < size; i++){
        for (var j = 0; j < size; j++){
            for (var n = 0; n < neighborCoords.length; n++){
                var row = i + neighborCoords[n][0]
                var col = j + neighborCoords[n][1]
                if (grid[row] && grid[row][col]){
                    grid[i][j].neighbors.push(grid[row][col])
                }
            }
        }
    }

    // infect the initial cells
    for (var i = 0; i < startingNum; i++){
        infect(cells[Math.floor(cells.length * Math.random())], 0)
    }
}

function loop(){
    requestAnimationFrame(loop)

    // for each cell marked as infected, set its infectionTime
    for (var i = 0; i < cells.length; i++){
        var p = cells[i]
        if (p.infect){
            p.infect = false
            p.infectionTime = infectionDuration
        }
    }

    for (var i = 0; i < cells.length; i++){
        var p = cells[i]

        // for each infected cell, decrement its timer
        if (p.infectionTime){
            p.infectionTime--

            // for each neighbor that isn't infected, if the probability is right and the neighbor isn't immune to that infection, infect it
            for (var n = 0; n < p.neighbors.length; n++){
                var neighbor = p.neighbors[n]
                if (!neighbor.infectionTime && Math.random() < infectionProbability){
                    var infection = Math.random() < mutationProbability ? mutation() : p.currentInfection
                    if (!neighbor.infectionCounts[infection] || neighbor.infectionCounts[infection] < maxInfections){
                        infect(neighbor, infection)
                    }
                }
            }

            // colors! yay!
            var color = colors[p.currentInfection % colors.length]
            imageData.data[4 * i + 0] = color[0]
            imageData.data[4 * i + 1] = color[1]
            imageData.data[4 * i + 2] = color[2]
        } else {
            imageData.data[4 * i + 0] = imageData.data[4 * i + 1] = imageData.data[4 * i + 2] = 0
        }

        imageData.data[4 * i + 3] = 255
    }

    T.putImageData(imageData, 0, 0)
}

// init canvas and go
C.width = C.height = size
T = C.getContext('2d')
reset()
loop()
Andrew
la source
1
Mettre l'infectionProbability à 1 a fait certains des modèles les plus doux que j'ai vus!
William Barbosa
Pourriez-vous ajouter combien de temps votre programme prend à votre réponse?
Beta Decay
7

C ++ 11, 6-8 minutes

Mon test dure environ 6 à 8 minutes sur ma machine Fedora 19, i5. Mais en raison du caractère aléatoire de la mutation, elle pourrait aussi bien être plus rapide ou prendre plus de temps. Je pense que les critères de notation doivent être revus.

Imprime le résultat sous forme de texte à la fin de l'opération, personne en bonne santé indiquée par un point ( .), personne infectée par un astérisque ( *), sauf si le ANIMATEdrapeau est défini sur vrai, auquel cas il affichera différents caractères pour les personnes infectées par une souche virale différente.

Voici un GIF pour 10x10, 200 périodes.

10x10Gif

Comportement de mutation

Chaque mutation donnera une nouvelle souche jamais vue auparavant (il est donc possible qu'une personne infecte les quatre personnes voisines avec 4 souches distinctes), à moins que 800 souches aient été générées, auquel cas aucun virus ne subira de mutation supplémentaire.

Le résultat de 8 minutes provient du nombre de personnes infectées suivantes:

Période 0, infectée: 4
Période 100, infectée: 53743
Période 200, infecté: 134451
Période 300, infectée: 173369
Période 400, infecté: 228176
Période 500, infecté: 261473
Période 600, infecté: 276086
Période 700, infecté: 265774
Période 800, infecté: 236828
Période 900, infectée: 221275

tandis que le résultat de 6 minutes provient des éléments suivants:

Période 0, infectée: 4
Période 100, infectée: 53627
Période 200, infecté: 129033
Période 300, infectée: 186127
Période 400, infecté: 213633
Période 500, infecté: 193702
Période 600, infectée: 173995
Période 700, infecté: 157966
Période 800, infecté: 138281
Période 900, infectée: 129381

Représentation des personnes

Chaque personne est représentée en 205 octets. Quatre octets pour stocker le type de virus contracté par cette personne, un octet pour enregistrer depuis combien de temps cette personne a été infectée et 200 octets pour stocker combien de fois il a contracté chaque souche de virus (2 bits chacun). Il y a peut-être un alignement d'octets supplémentaire effectué par C ++, mais la taille totale sera d'environ 200 Mo. J'ai deux grilles pour stocker la prochaine étape, donc au total, il utilise environ 400 Mo.

Je stocke l'emplacement des personnes infectées dans une file d'attente, pour réduire le temps requis dans les premières périodes (ce qui est vraiment utile jusqu'à des périodes <400).

Caractéristiques techniques du programme

Toutes les 100 étapes, ce programme imprimera le nombre de personnes infectées, sauf si l' ANIMATEindicateur est défini sur true, auquel cas il imprimera la grille entière toutes les 100 ms.

Cela nécessite des bibliothèques C ++ 11 (compiler en utilisant -std=c++11flag, ou dans Mac avec clang++ -std=c++11 -stdlib=libc++ virus_spread.cpp -o virus_spread).

Exécutez-le sans arguments pour les valeurs par défaut, ou avec des arguments comme celui-ci:

./virus_spread 1 0.01 1000

#include <cstdio>
#include <cstring>
#include <random>
#include <cstdlib>
#include <utility>
#include <iostream>
#include <deque>
#include <cmath>
#include <functional>
#include <unistd.h>

typedef std::pair<int, int> pair;
typedef std::deque<pair> queue;

const bool ANIMATE = false;
const int MY_RAND_MAX = 999999;

std::default_random_engine generator(time(0));
std::uniform_int_distribution<int> distInt(0, MY_RAND_MAX);
auto randint = std::bind(distInt, generator);
std::uniform_real_distribution<double> distReal(0, 1);
auto randreal = std::bind(distReal, generator);

const int VIRUS_TYPE_COUNT = 800;
const int SIZE = 1000;
const int VIRUS_START_COUNT = 4;

typedef struct Person{
    int virusType;
    char time;
    uint32_t immune[VIRUS_TYPE_COUNT/16];
} Person;

Person people[SIZE][SIZE];
Person tmp[SIZE][SIZE];
queue infecteds;

double transmissionProb = 1.0;
double mutationProb = 0.01;
int periods = 1000;

char inline getTime(Person person){
    return person.time;
}

char inline getTime(int row, int col){
    return getTime(people[row][col]);
}

Person inline setTime(Person person, char time){
    person.time = time;
    return person;
}

Person inline addImmune(Person person, uint32_t type){
    person.immune[type/16] += 1 << (2*(type % 16));
    return person;
}

bool inline infected(Person person){
    return getTime(person) > 0;
}

bool inline infected(int row, int col){
    return infected(tmp[row][col]);
}

bool inline immune(Person person, uint32_t type){
    return (person.immune[type/16] >> (2*(type % 16)) & 3) == 3;
}

bool inline immune(int row, int col, uint32_t type){
    return immune(people[row][col], type);
}

Person inline infect(Person person, uint32_t type){
    person.time = 1;
    person.virusType = type;
    return person;
}

bool inline infect(int row, int col, uint32_t type){
    auto person = people[row][col];
    auto tmpPerson = tmp[row][col];
    if(infected(tmpPerson) || immune(tmpPerson, type) || infected(person) || immune(person, type)) return false;
    person = infect(person, type);
    infecteds.push_back(std::make_pair(row, col));
    tmp[row][col] = person;
    return true;
}

uint32_t inline getType(Person person){
    return person.virusType;
}

uint32_t inline getType(int row, int col){
    return getType(people[row][col]);
}

void print(){
    for(int row=0; row < SIZE; row++){
        for(int col=0; col < SIZE; col++){
            printf("%c", infected(row, col) ? (ANIMATE ? getType(row, col)+48 : '*') : '.');
        }
        printf("\n");
    }
}

void move(){
    for(int row=0; row<SIZE; ++row){
        for(int col=0; col<SIZE; ++col){
            people[row][col] = tmp[row][col];
        }
    }
}

int main(const int argc, const char **argv){
    if(argc > 3){
        transmissionProb = std::stod(argv[1]);
        mutationProb = std::stod(argv[2]);
        periods = atoi(argv[3]);
    }
    int row, col, size;
    uint32_t type, newType=0;
    char time;
    Person person;
    memset(people, 0, sizeof(people));
    for(int row=0; row<SIZE; ++row){
        for(int col=0; col<SIZE; ++col){
            people[row][col] = {};
        }
    }
    for(int i=0; i<VIRUS_START_COUNT; i++){
        row = randint() % SIZE;
        col = randint() % SIZE;
        if(!infected(row, col)){
            infect(row, col, 0);
        } else {
            i--;
        }
    }
    move();
    if(ANIMATE){
        print();
    }
    for(int period=0; period < periods; ++period){
        size = infecteds.size();
        for(int i=0; i<size; ++i){
            pair it = infecteds.front();
            infecteds.pop_front();
            row = it.first;
            col = it.second;
            person = people[row][col];
            time = getTime(person);
            if(time == 0) continue;
            type = getType(person);
            if(row > 0 && randreal() < transmissionProb){
                if(newType < VIRUS_TYPE_COUNT-1 && randreal() < mutationProb){
                    newType++;
                    if(!infect(row-1, col, newType)) newType--;
                } else {
                    infect(row-1, col, type);
                }
            }
            if(row < SIZE-1 && randreal() < transmissionProb){
                if(newType < VIRUS_TYPE_COUNT-1 && randreal() < mutationProb){
                    newType++;
                    if(!infect(row+1, col, newType)) newType--;
                } else {
                    infect(row+1, col, type);
                }
            }
            if(col > 0 && randreal() < transmissionProb){
                if(newType < VIRUS_TYPE_COUNT-1 && randreal() < mutationProb){
                    newType++;
                    if(!infect(row, col-1, newType)) newType--;
                } else {
                    infect(row, col-1, type);
                }
            }
            if(col < SIZE-1 && randreal() < transmissionProb){
                if(newType < VIRUS_TYPE_COUNT-1 && randreal() < mutationProb){
                    newType++;
                    if(!infect(row, col+1, newType)) newType--;
                } else {
                    infect(row, col+1, type);
                }
            }
            time += 1;
            if(time == 4) time = 0;
            person = setTime(person, time);
            if(time == 0){
                person = addImmune(person, type);
            } else {
                infecteds.push_back(std::make_pair(row, col));
            }
            tmp[row][col] = person;
        }
        if(!ANIMATE && period % 100 == 0) printf("Period %d, Size: %d\n", period, size);
        move();
        if(ANIMATE){
            printf("\n");
            print();
            usleep(100000);
        }
    }
    if(!ANIMATE){
        print();
    }
    return 0;
}
juste la moitié
la source
J'aime vraiment ça! Ma seule question est de savoir comment créer le GIF?
Beta Decay
1
J'utilise cet outil: linux.die.net/man/1/byzanz-record . Actuellement, il n'a pas d'interface graphique, vous devrez donc utiliser la ligne de commande = D
juste
Oh, c'est bien, merci! :)
Beta Decay
3

C # 6-7 minutes

Modifier 2

J'ai finalement (5 heures) généré une sortie détaillée pendant près de 1000 périodes (seulement 840 images, puis il s'est écrasé) à 1000x1000, toutes les 1 période, mais il est proche de 160 Mo et nécessite toute la mémoire de mon système pour s'afficher (IrfanView) , même pas sûr que cela fonctionnerait dans un navigateur, puis-je le mettre plus tard.

ÉDITER

J'ai passé beaucoup de temps à rendre cela plus efficace selon la réponse de "Beta Decay" en disant "Choisissez la souche au hasard" J'ai choisi uniquement la méthode aléatoire pour choisir qui infecte qui par période, mais j'ai changé la façon de calculer et mis en place tout, j'ai mis à jour mes messages avec les nouveaux détails.

Codé mon estimation la plus proche de ce que je pouvais, j'espère que cela suit toutes les règles, il utilise une tonne de mémoire sur mon système (environ 1,2 Go). Le programme peut produire des gifs animés (l'air cool, vraiment lent) ou simplement une image correspondant aux spécifications de "Beta Decay". C'est un peu la réinvention de la roue, mais ça a vraiment l'air cool:


Résultats

(Remarque: cela ne fait que la différence entre infecté et non infecté, c'est-à-dire non verbeux)

1000 périodes, 1% de taux de mutation, 100% de propagation:

Résultat

Exemples (verbeux)

Quoi qu'il en soit, utiliser 100% de "probabilité de transmission" en mode non verbeux est un peu ennuyeux car vous obtenez toujours les mêmes formes et vous ne pouvez pas voir les différentes mutations, si vous ajustez les paramètres autour d'un bit (et activez le mode verbeux) vous obtenez une sortie cool (les GIF animés s'affichent toutes les 10 images):

Aléatoire - Taille de la grille: 200, ProbTransmission: 100%, ProbMutation: 1%

100Précent

Aléatoire - Taille de la grille: 200, ProbTransmission: 20%, ProbMutation: 1%

20Précent

Notation

Je suis d'accord avec "justhalf" que les critères de notation peuvent ne pas être équitables car chaque course sera différente en raison du caractère aléatoire des mutations et de la position des points de départ aléatoires. Peut-être que nous pourrions faire en moyenne plusieurs runs ou quelque chose comme ça ..., bon de toute façon c'est en C # donc bounty pour moi :( de toute façon.

Code

Assurez-vous d'inclure la bibliothèque MagickImage (définie pour compiler x64 bits) sinon elle ne sera pas construite ( http://pastebin.com/vEmPF1PM ):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Imaging;
using ImageMagick;
using System.IO;

namespace Infection
{
    class Program
    {
        #region Infection Options
        private const double ProbabilityOfTransmission = .2;
        private const double ChanceOfMutation = 0.01;
        private const Int16 StageSize = 1000;
        private const Int16 MaxNumberOfMutations = 800;
        private const byte MaxInfectionTime = 3;
        private const byte NumberOfPeopleToRandomlyInfect = 4;
        private static int NumberOfPeriods = 1000;
        #endregion Infection Options

        #region Run Options
        private const bool VerbosMode = false;        
        private const int ImageFrequency = 10;
        #endregion Run Options

        #region Stage        
        private static Int16 MutationNumber = 1;

        private class Person
        {
            public Person()
            {
                PreviousInfections = new Dictionary<Int16, byte>();
                InfectionTime = 0;
                CurrentInfection = 0;
                PossibleNewInfections = new List<short>(4);
            }
            public Dictionary<Int16, byte> PreviousInfections { get; set; }
            public byte InfectionTime { get; set; }
            public Int16 CurrentInfection { get; set; }
            public List<Int16> PossibleNewInfections { get; set; }
        }
        private static Person[][] Stage = new Person[StageSize][];
        #endregion Stage

        static void Main(string[] args)
        {
            DateTime start = DateTime.UtcNow;

            //Initialize stage
            for (Int16 i = 0; i < Stage.Length; i++)
            {
                var tmpList = new List<Person>();
                for (Int16 j = 0; j < Stage.Length; j++)
                    tmpList.Add(new Person());
                Stage[i] = tmpList.ToArray();
            }

            //Randomly infect people
            RandomlyInfectPeople(NumberOfPeopleToRandomlyInfect);

            //Run through the periods(NumberOfPeriods times)
            List<MagickImage> output = new List<MagickImage>();
            while (NumberOfPeriods > 0)
            {
                //Print details(verbose)                
                if (VerbosMode && NumberOfPeriods % ImageFrequency == 0)
                {
                    Console.WriteLine("Current Number: " + NumberOfPeriods);
                    Console.WriteLine("Current Mutation: " + MutationNumber);
                    output.Add(BoardToImage());
                }

                Period();
            }

            //Outputs a Animated Gif(verbose)
            if (VerbosMode)
            {
                ImagesToAnimatedGIF(output.ToArray(), Directory.GetCurrentDirectory() + "\\Output.gif");
                System.Diagnostics.Process.Start(Directory.GetCurrentDirectory() + "\\Output.gif");
            }
            //Only outputs the basic result image matching the specs
            SaveBoardToSimpleImage(Directory.GetCurrentDirectory() + "\\FinalState.gif");

            Console.WriteLine("Total run time in seconds: " + (DateTime.UtcNow - start).TotalSeconds);
            Console.WriteLine("Press enter to exit");
            Console.ReadLine();
        }

        #region Image
        private static void SaveBoardToSimpleImage(string filepath)
        {
            using (Bitmap img = new Bitmap(StageSize, StageSize))
            {
                for (int i = 0; i < img.Width; i++)
                    for (int j = 0; j < img.Height; j++)
                        img.SetPixel(i, j, Stage[i][j].CurrentInfection == 0 ? Color.FromArgb(255, 255, 255) :
                            Color.FromArgb(64, 255, 0));
                img.Save(filepath, ImageFormat.Gif);
            }
        }
        private static MagickImage BoardToImage()
        {
            using (Bitmap img = new Bitmap(StageSize, StageSize))
            {
                for (int i = 0; i < img.Width; i++)
                    for (int j = 0; j < img.Height; j++)
                        img.SetPixel(i, j, Stage[i][j].CurrentInfection == 0 ? Color.White :
                            Color.FromArgb(Stage[i][j].CurrentInfection % 255,
                            Math.Abs(Stage[i][j].CurrentInfection - 255) % 255,
                            Math.Abs(Stage[i][j].CurrentInfection - 510) % 255));
                return new MagickImage(img);
            }
        }
        private static void ImagesToAnimatedGIF(MagickImage[] images, string filepath)
        {
            using (MagickImageCollection collection = new MagickImageCollection())
            {
                foreach (var image in images)
                {
                    collection.Add(image);
                    collection.Last().AnimationDelay = 20;
                }
                collection.Write(filepath);
            }
        }
        #endregion Image

        #region Infection
        private static void Period()
        {
            Infect();
            ChooseRandomInfections();
            IncrementDiseaseProgress();
            Cure();

            NumberOfPeriods--;
        }
        private static void Cure()
        {
            Parallel.For(0, Stage.Length, i =>
            {
                for (Int16 j = 0; j < Stage.Length; j++)
                    if (Stage[i][j].CurrentInfection != 0 && Stage[i][j].InfectionTime == MaxInfectionTime + 1)
                    {
                        //Add disease to already infected list
                        if (Stage[i][j].PreviousInfections.ContainsKey(Stage[i][j].CurrentInfection))
                            Stage[i][j].PreviousInfections[Stage[i][j].CurrentInfection]++;
                        else
                            Stage[i][j].PreviousInfections.Add(Stage[i][j].CurrentInfection, 1);

                        //Cure
                        Stage[i][j].InfectionTime = 0;
                        Stage[i][j].CurrentInfection = 0;
                    }
            });
        }
        private static void IncrementDiseaseProgress()
        {
            Parallel.For(0, Stage.Length, i =>
            {
                for (Int16 j = 0; j < Stage.Length; j++)
                    if (Stage[i][j].CurrentInfection != 0)
                        Stage[i][j].InfectionTime++;
            });
        }
        private static void RandomlyInfectPeople(Int16 numberOfPeopleToInfect)
        {
            var randomList = new List<int>();
            while (randomList.Count() < numberOfPeopleToInfect * 2)
            {
                randomList.Add(RandomGen2.Next(StageSize));
                randomList = randomList.Distinct().ToList();
            }
            while (randomList.Count() > 0)
            {
                Stage[randomList.Last()][randomList[randomList.Count() - 2]].CurrentInfection = MutationNumber;
                Stage[randomList.Last()][randomList[randomList.Count() - 2]].InfectionTime = 1;
                randomList.RemoveAt(randomList.Count() - 2);
                randomList.RemoveAt(randomList.Count() - 1);
            }
        }
        private static void Infect()
        {
            Parallel.For(0, Stage.Length, i =>
            {
                for (Int16 j = 0; j < Stage.Length; j++)
                    InfectAllSpacesAround((short)i, j);
            });
        }
        private static void InfectAllSpacesAround(Int16 x, Int16 y)
        {
            //If not infected or just infected this turn return
            if (Stage[x][y].CurrentInfection == 0 || (Stage[x][y].CurrentInfection != 0 && Stage[x][y].InfectionTime == 0)) return;

            //Infect all four directions(if possible)
            if (x > 0)
                InfectOneSpace(Stage[x][y].CurrentInfection, (short)(x - 1), y);

            if (x < Stage.Length - 1)
                InfectOneSpace(Stage[x][y].CurrentInfection, (short)(x + 1), y);

            if (y > 0)
                InfectOneSpace(Stage[x][y].CurrentInfection, x, (short)(y - 1));

            if (y < Stage.Length - 1)
                InfectOneSpace(Stage[x][y].CurrentInfection, x, (short)(y + 1));
        }
        private static void InfectOneSpace(Int16 currentInfection, Int16 x, Int16 y)
        {
            //If the person is infected, or If they've already been infected "MaxInfectionTime" then don't infect
            if (Stage[x][y].CurrentInfection != 0 || (Stage[x][y].PreviousInfections.ContainsKey(currentInfection) &&
                    Stage[x][y].PreviousInfections[currentInfection] >= MaxInfectionTime)) return;

            //If random is larger than change of transmission don't transmite disease
            if (RandomGen2.Next(100) + 1 > ProbabilityOfTransmission * 100) return;

            //Possible mutate
            if (MutationNumber <= MaxNumberOfMutations && RandomGen2.Next(100) + 1 <= ChanceOfMutation * 100)
                lock (Stage[x][y])
                {
                    MutationNumber++;
                    Stage[x][y].PossibleNewInfections.Add(MutationNumber);
                }
            //Regular infection
            else
                lock (Stage[x][y])
                    Stage[x][y].PossibleNewInfections.Add(currentInfection);

        }
        private static void ChooseRandomInfections()
        {
            Parallel.For(0, Stage.Length, i =>
            {
                for (Int16 j = 0; j < Stage.Length; j++)
                {
                    if (Stage[i][j].CurrentInfection != 0 || !Stage[i][j].PossibleNewInfections.Any()) continue;
                    Stage[i][j].CurrentInfection = Stage[i][j].PossibleNewInfections[RandomGen2.Next(Stage[i][j].PossibleNewInfections.Count)];
                    Stage[i][j].PossibleNewInfections.Clear();
                    Stage[i][j].InfectionTime = 0;
                }
            }
            );
        }
        #endregion Infection
    }

    //Fancy Schmancy new random number generator for threaded stuff, fun times
    //http://blogs.msdn.com/b/pfxteam/archive/2009/02/19/9434171.aspx
    public static class RandomGen2
    {
        private static Random _global = new Random();
        [ThreadStatic]
        private static Random _local;

        public static int Next()
        {
            Random inst = _local;
            if (inst == null)
            {
                int seed;
                lock (_global) seed = _global.Next();
                _local = inst = new Random(seed);
            }
            return inst.Next();
        }

        public static int Next(int input)
        {
            Random inst = _local;
            if (inst == null)
            {
                int seed;
                lock (_global) seed = _global.Next();
                _local = inst = new Random(seed);
            }
            return inst.Next(input);
        }
    }
}
David Rogers
la source