Hãy là một nhà dịch tễ học!


13

Thử thách

Bạn phải tạo ra một mô hình đơn giản về cách bệnh lây lan xung quanh một nhóm người.

Quy tắc và yêu cầu

Mô hình phải là một mảng 1000 x 1000 2D với mỗi phần tử là một người khác nhau.

Người dùng phải nhập ba biến bằng cách sử dụng argv: xác suất truyền (khả năng ai đó sẽ lây nhiễm cho người khác), cơ hội đột biến và bao nhiêu giai đoạn mô phỏng nên chạy.

Trong giai đoạn đầu tiên ( t=0), bốn người nên được chọn ngẫu nhiên và bị nhiễm bệnh.

Cách thức hành xử của bệnh được điều chỉnh bởi các quy tắc sau:

  • Bệnh chỉ có thể di chuyển theo chiều dọc và chiều ngang, di chuyển sang người bên cạnh.
  • Nhiễm trùng kéo dài trong 3 thời kỳ ở mỗi người. Bạn có thể không yếu tố suy giảm miễn dịch.
  • Sau khi một người bị nhiễm ba lần, họ miễn dịch và không thể bị nhiễm lại.
  • Bệnh có thể bị đột biến làm cho những người miễn dịch trước đây dễ bị tổn thương với căn bệnh đột biến mới này. Bệnh đột biến có những đặc điểm giống hệt nhau và tuân theo các quy tắc giống như bệnh ban đầu.
  • Nếu đột biến xảy ra, toàn bộ bệnh không thay đổi, chỉ là "gói" cụ thể đó khi truyền.
  • Khi một người đã bị nhiễm một loại vi-rút, họ không thể bị nhiễm lại cho đến khi nhiễm trùng hiện tại qua đi.
  • Nếu một người bị nhiễm bệnh, họ bị nhiễm bệnh từ khi bắt đầu giai đoạn nhiễm bệnh cho đến khi kết thúc.
  • Không có mức độ miễn dịch - một người có thể miễn dịch hoặc không.
  • Để dừng quá tải bộ nhớ, có giới hạn tối đa 800 đột biến.

Khi kết thúc số lượng thời gian quy định, bạn nên xuất kết quả.

Kết quả phải là lưới 1000 x 1000 cho biết người nào bị nhiễm và người nào không bị nhiễm. Đây có thể là đầu ra dưới dạng tệp văn bản, dưới dạng tệp hình ảnh hoặc đầu ra đồ họa (trong đó #FFFFFF là người khỏe mạnh và # 40FF00 là người bị nhiễm bệnh).

Xin vui lòng bạn có thể bao gồm tên ngôn ngữ và một lệnh để chạy nó trong câu trả lời của bạn.

Chiến thắng

Mã nhanh nhất để chạy trên máy tính của tôi thắng. Thời gian của nó sẽ được đo bằng đoạn mã Python sau:

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

Lưu ý rằng khi chạy tập lệnh này, tôi sẽ sử dụng các giá trị mặc định sau:

Probability of transmission = 1
Chance of mutation = 0.01
Number of periods = 1000

3
Bạn muốn tạo một tệp 10 gigabyte ?
Ypnypn

1
Bằng cách có giới hạn 4 GB, bạn đã loại bỏ hoàn toàn tùy chọn lưu kết quả đầu ra trong tệp hình ảnh ...
Trình tối ưu hóa

10
1000x1000 : Giống như vậy hơn!
COTO

1
Cũng nói có hai người cạnh nhau. Virus hợp đồng đầu tiên V, virus hợp đồng thứ hai V'. Sự co thắt sẽ kết thúc ở cùng một thời kỳ. Virus có thể Vlây nhiễm cho người thứ hai? (Hoặc một câu hỏi trắng đen hơn: có thể một người bị nhiễm bệnh ngay sau khi anh ta được chữa lành, vì vậy anh ta sẽ kết thúc với 6 lần nhiễm trùng liên tiếp?)
justhalf

1
Một số khác, hai virus độc lập có thể biến đổi thành cùng một virus không? Nói rằng chúng ta có Vtrong người A, và Vmột lần nữa trong người B. Khi chúng truyền virut, cả hai có thể biến đổi thành cùng một đột biến V'không? Hoặc có lẽ trong thực tế họ nên đột biến với cùng một chủng virus? Nếu chúng có thể đột biến tùy ý, xác suất hai virus biến đổi thành cùng một chủng virus là bao nhiêu?
cần

Câu trả lời:


10

Tôi tò mò xem nó sẽ trông như thế nào nên tôi đã thực hiện bản hack nhanh và bẩn này trong 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()

1
Đặt nhiễm trùng Khả năng di chuyển thành 1 đã tạo ra một số mẫu ngọt ngào nhất tôi từng thấy!
William Barbosa

Bạn có thể thêm thời gian chương trình của bạn mất bao lâu để trả lời?
Beta Decay

7

C ++ 11, 6-8 phút

Quá trình chạy thử của tôi mất khoảng 6-8 phút trong máy Fedora 19, i5 của tôi. Nhưng do tính ngẫu nhiên của đột biến, nó có thể nhanh hơn hoặc mất nhiều thời gian hơn thế. Tôi nghĩ rằng các tiêu chí chấm điểm cần phải được đánh giá lại.

In kết quả dưới dạng văn bản khi kết thúc hoàn thành, người khỏe mạnh được biểu thị bằng dấu chấm ( .), người bị nhiễm bởi dấu hoa thị ( *), trừ khi ANIMATEcờ được đặt thành đúng, trong trường hợp đó, nó sẽ hiển thị các ký tự khác nhau cho những người bị nhiễm các chủng virus khác nhau.

Đây là một GIF cho 10 x 10, 200 tiết.

10 x 10

Hành vi đột biến

Mỗi đột biến sẽ tạo ra chủng mới chưa từng thấy trước đây (vì vậy có thể một người lây nhiễm cho bốn người lân cận với 4 chủng khác nhau), trừ khi 800 chủng đã được tạo ra, trong trường hợp đó, virus sẽ không bị đột biến thêm nữa.

Kết quả 8 phút đến từ số người nhiễm bệnh sau đây:

Thời kỳ 0, bị nhiễm bệnh: 4
Thời kỳ 100, bị nhiễm bệnh: 53743
Thời kỳ 200, bị nhiễm bệnh: 134451
Thời kỳ 300, bị nhiễm bệnh: 173369
Thời kỳ 400, bị nhiễm bệnh: 228176
Kỳ 500, bị nhiễm: 261473
Thời kỳ 600, bị nhiễm bệnh: 276086
Thời kỳ 700, bị nhiễm bệnh: 265774
Thời kỳ 800, bị nhiễm bệnh: 236828
Thời kỳ 900, bị nhiễm bệnh: 221275

trong khi kết quả 6 phút đến từ các mục sau:

Thời kỳ 0, bị nhiễm bệnh: 4
Thời kỳ 100, bị nhiễm bệnh: 53627
Thời kỳ 200, bị nhiễm bệnh: 129033
Thời kỳ 300, bị nhiễm bệnh: 186127
Thời kỳ 400, bị nhiễm bệnh: 213633
Thời kỳ 500, bị nhiễm bệnh: 193702
Thời kỳ 600, bị nhiễm bệnh: 173995
Thời kỳ 700, bị nhiễm bệnh: 157966
Giai đoạn 800, bị nhiễm bệnh: 138281
Thời kỳ 900, bị nhiễm bệnh: 129381

Đại diện cá nhân

Mỗi người được đại diện trong 205 byte. Bốn byte để lưu trữ loại vi-rút mà người này đang mắc phải, một byte để lưu trữ người này đã bị nhiễm bệnh bao lâu và 200 byte để lưu trữ số lần anh ta nhiễm mỗi chủng vi-rút (mỗi loại 2 bit). Có lẽ có một số căn chỉnh byte bổ sung được thực hiện bởi C ++, nhưng tổng kích thước sẽ vào khoảng 200MB. Tôi có hai lưới để lưu trữ bước tiếp theo, vì vậy tổng cộng nó sử dụng khoảng 400MB.

Tôi lưu trữ vị trí của những người bị nhiễm trong hàng đợi, để cắt giảm thời gian cần thiết trong giai đoạn đầu (điều này thực sự hữu ích cho đến khoảng thời gian <400).

Chương trình kỹ thuật

Cứ sau 100 bước chương trình này sẽ in số người bị nhiễm, trừ khi ANIMATEcờ được đặt thành true, trong trường hợp đó, nó sẽ in toàn bộ lưới sau mỗi 100ms.

Điều này đòi hỏi các thư viện C ++ 11 (biên dịch bằng -std=c++11cờ hoặc trong Mac với clang++ -std=c++11 -stdlib=libc++ virus_spread.cpp -o virus_spread).

Chạy nó mà không có đối số cho các giá trị mặc định hoặc với các đối số như thế này:

./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;
}

Tôi thực sự thích điều này! Câu hỏi duy nhất của tôi là làm thế nào để bạn làm cho GIF?
Beta Decay

1
Tôi đang sử dụng công cụ này: linux.die.net/man/1/byzanz-record . Hiện tại nó không có GUI, vì vậy bạn sẽ cần sử dụng dòng lệnh = D
justhalf

Ồ, thật tuyệt, cảm ơn! :)
Beta Decay

3

C # 6-7 phút

Chỉnh sửa 2

Cuối cùng tôi (5 giờ) đã tạo ra một đầu ra dài dòng cho gần 1000 giai đoạn (chỉ có 8 khung hình sau đó bị sập) ở 1000x1000, cứ sau 1 chu kỳ, tuy nhiên gần 160 MB và yêu cầu tất cả bộ nhớ trên hệ thống của tôi hiển thị (IrfanView) , thậm chí không chắc chắn rằng nó sẽ hoạt động trong trình duyệt, tôi có thể đưa nó lên sau.

BIÊN TẬP

Tôi đã dành hết thời gian để thực hiện việc này hiệu quả hơn cho mỗi câu trả lời của "Phân rã Beta" nêu rõ "Chọn chủng một cách ngẫu nhiên" Tôi chỉ chọn phương pháp ngẫu nhiên để chọn ai lây nhiễm cho ai trong mỗi giai đoạn, tuy nhiên tôi đã thay đổi cách tính và xâu chuỗi mọi thứ, tôi đã cập nhật bài viết của mình với các chi tiết mới.

Mã hóa ước tính gần nhất của tôi với điều này tôi có thể, tôi hy vọng nó tuân theo tất cả các quy tắc, nó sử dụng một tấn bộ nhớ trên hệ thống của tôi (khoảng 1,2 GB). Chương trình có thể xuất các gif hoạt hình (trông rất tuyệt, rất chậm) hoặc chỉ là một hình ảnh phù hợp với thông số kỹ thuật của "Phân rã Beta". Đây là một chút sáng tạo lại bánh xe, nhưng chắc chắn trông rất tuyệt:


Các kết quả

(Lưu ý: điều này chỉ phân biệt giữa nhiễm và không nhiễm, tức là không dài dòng)

1000 chu kỳ, tỷ lệ đột biến 1%, chênh lệch 100%:

Kết quả

Ví dụ (Verbose)

Dù sao, sử dụng 100% "Xác suất truyền" trong chế độ không dài dòng là nhàm chán vì bạn luôn có cùng hình dạng và bạn không thể thấy các đột biến khác nhau, nếu bạn điều chỉnh các tham số xung quanh một bit (và bật chế độ dài dòng) bạn nhận được một số kết quả tìm kiếm thú vị (GIF động hiển thị mỗi khung hình thứ 10):

Ngẫu nhiên - Kích thước lưới: 200, Xác thực: 100%, Xác suất: 1%

100 phần trăm

Ngẫu nhiên - Kích thước lưới: 200, Xác thực: 20%, Xác suất: 1%

20

Chấm điểm

Tôi đồng ý với "justhalf" rằng các tiêu chí chấm điểm có thể không công bằng vì mỗi lần chạy sẽ khác nhau do tính ngẫu nhiên của các đột biến và vị trí của các điểm bắt đầu ngẫu nhiên. Có lẽ chúng ta có thể thực hiện trung bình một vài lần chạy hoặc một cái gì đó tương tự ..., dù sao thì đây cũng là C # vì vậy tiền thưởng cho tôi :( dù sao đi nữa.

Đảm bảo bao gồm thư viện MagickImage (được đặt để biên dịch x64 bit) nếu không nó sẽ không được xây dựng ( 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);
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.