Ліс - імітаційна екосистема


19

ПРИМІТКА

Ця проблема була взята з цієї теми reddit (сповіщення спойлера!), І я її відкоригував, щоб вона відповідала формату цього сайту. Весь кредит належить користувачеві reddit "Coder_d00d".

У цій проблемі ми будемо імітувати ліс.

Для цього імітованого лісу ми матимемо справу з 3 аспектами.

  • Дерева, які можуть бути деревом саджанця, деревом або бузиною.
  • Дроворуби (Він рубає дерева, він їсть свій обід і їде в спробу Лави)
  • Ведмеді (Він манить дровами, які пахнуть млинцями)

Попередження: ці правила, швидше за все, не є досконалими. Розгляньте їх як настанову, і якщо вам потрібно трохи підправити все, що є нормальним (швидкість нересту було вказано як проблему, див. Відповідь kuroi neko як приклад цього.

Цикл часу:

Моделювання буде імітувати по місяцях. Ви будете прогресивно вперед за допомогою "галочки". Кожна «галочка» представляє місяць. Кожні 12 "тиків" представляє рік. Наш ліс зміниться і буде постійно змінюватися. Ми запишемо хід нашого лісу та проаналізуємо, що з ним відбувається.

Ліс:

Ліс буде двовимірним лісом. Нам потрібно буде ввести N, щоб представити розмір лісу в сітці, розміром якого є N x N. У кожному місці ви можете утримувати дерева, ведмеді чи дроворуби. Вони можуть займати одне і те ж місце, але часто події відбуваються, коли вони займають одне і те ж місце.

Наш ліс буде породжений випадковим чином залежно від розміру. Наприклад, якщо ваше значення N = 10. У вас буде ліс 10 на 10 та 100 плям.

  • 10% Лісу утримуватиме Дроворуб у 10 випадкових місцях. (використовуючи наш 100 лісових плям, це має бути 10 лісорубів)
  • 50% Лісу буде містити Дерева (Дерева можуть бути одного з 3-х видів і починатимуться як середнє дерево "Дерева") у випадкових місцях.
  • 2% Лісу утримуватимуть Ведмеді.

Як ви отримаєте розмір лісу, залежить від вас (прочитайте з stdin, файлу або жорсткого коду в ньому). Я рекомендую зберігати N як 5 або вище. Маленькі ліси не дуже веселяться.

Події:

Під час моделювання відбудуться події. Події відбуваються на основі певної логіки, яку я поясню нижче. Я опишу події нижче в кожному описі 3 елементів нашого лісу.

Події впорядковані за порядком дерев, по-перше, дроворуби вдруге і ведмеді останні.

Дерева:

  • Щомісяця Дерево має 10% шанс породити нову «Саджанець». У випадковому відкритому просторі, що прилягає до Дерева, у вас є 10% шансів створити "Саджанець".

  • Наприклад, Дерево посеред лісу має ще 8 плям навколо нього. Одним із таких (якщо вони будуть порожніми) стане «Саджанець».

  • Через 12 місяців існування «Саджанець» буде оновлений до «Дерева». "Саджанець" не може нерестити інші дерева, поки він не дозріє в "Дерево".

  • Після того як "Саджанець" стає деревом, він може нерестити нові "Саджанці".

  • Коли "Дерево" буде близько 120 місяців (10 років), воно стане "Деревом старійшини".

  • Дерева старійшини мають 20% шансів породити новий "Саджанець" замість 10%.

  • Якщо до Дерева чи Старечого дерева немає відкритих плям, воно не породить нових дерев.

Дроворуби:

Дроворуби вирубають дерева, вони пропускають і стрибають, люблять натискати на польові квіти.

  • Щомісяця лесоруби будуть бродити. Вони рухатимуться до 3 разів на випадково вибране місце, яке знаходиться поруч у будь-якому напрямку. Так, наприклад, Дроворуб в середині вашої сітки має 8 плям для переміщення. Він поблукає до випадкового місця. Потім знову. І нарешті втретє. Вказівка: Це може бути будь-яке місце (тому вони можуть зайти в ведмедів, що призведе до мауля).

  • Коли лесоруб рухається, якщо він зустріне дерево (а не саджанець), він зупиниться, і його поневіряння на цей місяць закінчуються. Потім він заготовить Дерево для пиломатеріалів. Видаліть дерево. Наберіть 1 шматок пиломатеріалів.

  • Дроворуби не будуть збирати "Саджанці".

  • Лушники також збирають старші дерева. Дерева старійшини варті 2 пиломатеріалів.

Відстеження пиломатеріалів:

Кожні 12 місяців кількість заготовленого пиломатеріалу порівнюється з кількістю лісорубів у лісі.

  • Якщо зібраний пиломатеріал дорівнює або перевищує кількість лісорубів у лісі, наймаються нові лісоруби та випадковим чином нерестуються в лісі.

  • Визначте кількість лісорубів, які потрібно найняти: floor(lumber_collected / number_of_lumberjacks)

  • Однак якщо після 12-місячного проміжку кількість зібраного пиломатеріалів не перевищує кількості лісорубів, то дроворуб відпускають, щоб заощадити гроші, а 1 випадковий лесоруб вилучають із лісу. Зауважте, що ви ніколи не знижуватимете свою робочу силу нижче 0.

Ведмеді:

Ведмеді блукають лісом так само, як лесоруб. Однак замість 3 простору Ведмідь буде бродити до 5 просторів.

  • Якщо ведмідь натрапить на Дроворуба, він припинить свої поневіряння на місяць. (Наприклад, після 2-х переміщень ведмідь висаджується на простір з дроворубом, за цей місяць він більше не буде рухатися)

  • Лесоруби пахнуть млинцями. Ведмеді люблять млинці. Тому Ведмідь, на жаль, пошкодить і пошкодить дроворуба. Дроворуб буде вивезений із лісу (Він піде додому і по магазинах по середах і буде змазаний ковтками для чаю).

  • Ми будемо відстежувати це як аварію "Маула".

  • Зауважте, що чисельність лісоруба ніколи не може опускатися нижче 1 - тому якщо останній лесоруб манерується, просто нерестується інший.

Відстеження Maul:

  • Протягом 12 місяців, якщо трапиться 0 аварій на «Маулі», популяція ведмедів збільшиться на 1. Якщо все-таки трапляються аварії на «Маулі», лісоруби найнять зоопарк, щоб забрати та вивезти ведмедя. Видаліть 1 випадкового ведмедя. Зауважте, що якщо ваша популяція Ведмедя досягне 0 ведмедів, то в наступному році аварій "Маула" не буде, і тому ви породите 1 нового ведмедя в наступному році.

  • Якщо в лісі є лише 1 дроворуб, і він потрапить на Мауледа, його відправлять додому, але новий буде негайно найнятий і відтворений десь в лісі. Чисельність лісоруба ніколи не може опуститися нижче 1.

Час:

Моделювання відбувається протягом 4800 місяців (400 років), або поки немає Саджанців, Дерев чи Старших Дерев.

Вихід:

Щомісяця ви будете роздруковувати карту лісу - можливо, використовуючи карту ASCII або використовуючи графіку та кольори.

Необов'язкові додатки

  • Ви можете виводити популяції дерев, лісорубів та ведмедів кожного кліща.
  • Ви можете виводити щоразу, коли трапляється подія (наприклад: "Ведмідь помахував дроворубом".)

Оцінка балів

Це змагання за популярність, тому більшість результатів виграє!

EDIT - Люди вказали на ряд недоліків у моїх правилах, і, хоча ви можете сміливо задавати мені питання, також добре налаштувати правила, щоб вони відповідали вашій власній програмі чи інтерпретації програми.


Про: Note that you will never reduce your Lumberjack labor force below 0у пункті 3 розділу списку лісорубів, можливо, змініть це на 1, щоб відповідати тому, що ви згадуєте в розділі ведмедя?
Теун Пронк

Добрий момент, відредагуйте це зараз.
Джеймс Вільямс

2
Чи можете ви обговорити терміни руху? Це "дерева рухаються / просуваються", потім "лісоруби рухаються", потім "ведмеді рухаються"? Також, чи можуть ведмеді та дерева займати однакові простори?
durron597

1
Що станеться, якщо два ведмеді обидва входять в одне місце? А як щодо двох лесорубів? Чи можуть вони опинитися в одному місці?
Джеррі Єремія

1
Він ідентичний reddit.com/r/dailyprogrammer/comments/27h53e/… Ви повинні дати йому кредит - принаймні такий, щоб люди могли піти туди за іншими цікавими рішеннями.

Відповіді:


15

Javascript + HTML - спробуйте

Оновлено відповідно до популярного запиту

ліс та граф

Загальна поведінка

Зараз програма дещо інтерактивна.
Вихідний код повністю параметризований, тому ви можете налаштувати ще кілька внутрішніх параметрів за допомогою улюбленого текстового редактора.

Можна змінити розмір лісу.
Потрібно мінімум 2, щоб було достатньо місця для розміщення дерева, дроворуба та ведмедя на 3 різних місцях, а максимум довільно фіксується до 100 (що змусить ваш середній комп'ютер сканувати).

Ви також можете змінити швидкість моделювання.
Дисплей оновлюється кожні 20 мс, тому більший крок часу створить більш тонку анімацію.

Кнопки дозволяють зупинити / запустити моделювання або запустити його протягом місяця чи року.

Рух мешканців лісу зараз дещо оживлений. Події маулінгу та вирубки дерев також розраховані.

Відображається також журнал деяких подій. Ще кілька повідомлень доступні, якщо ви зміните рівень багатослів’я, але це затопить вас сповіщеннями "Боб ріже ще одне дерево".
Я б краще не робив цього, якби ти був, але я не так, так ...

Поруч з дитячим майданчиком намальований набір графіків із автоматичним масштабуванням:

  • популяції ведмедів та лісорубів
  • загальна кількість дерев, поділених на саджанці, зрілі та старі дерева

У легенді також відображаються поточні кількості кожного товару.

Стабільність системи

На графіках видно, що початкові умови не так масштабно масштабуються. Якщо ліс занадто великий, занадто багато ведмедів зменшують популяцію лісорубів, поки достатня кількість любителів млинців не буде поставлена ​​за ґрати. Це спричиняє початковий вибух старих дерев, що, в свою чергу, допомагає популяції лесорубів відновитись.

Здається, 15 - це мінімальний розмір для виживання лісу. Ліс розміром 10, як правило, буде зруйнований через кілька сотень років. Будь-який розмір понад 30 створить карту, майже повну дерев. Між 15 і 30 можна помітити, що популяція дерев значно коливається.

Деякі суперечливі правила правила

У коментарях до оригінальної публікації, схоже, різні двоногі не повинні займати одне й те саме місце. Це якимось чином суперечить правилу про бадилля, яке блукає на любителя млинців.
У будь-якому випадку я не дотримувався цієї настанови. Будь-яка лісова клітина може вмістити будь-яку кількість мешканців (і рівно нуль або одне дерево). Це може мати певні наслідки для ефективності дроворуба: я підозрюю, що це дозволяє їм легше копатися до грудок старих дерев. Що стосується ведмедів, я не очікую, що це дуже зміниться.

Я також вирішив завжди мати принаймні один дроворуб у лісі, незважаючи на те, що популяція старого дерева може досягати нуля (вистрілення останнього дроворуба на карті, якщо врожай дійсно поганий, що ніколи не відбудеться, якщо ліс не буде рубати до вимирання).

Налаштування

Для досягнення стабільності я додав два параметра налаштування:

1) швидкість росту дроворубів

коефіцієнт, застосований до формули, який дає кількість найманих зайвих лісорубів, коли вистачає деревини. Встановіть значення 1, щоб повернутися до початкового визначення, але я виявив, що значення приблизно .5 дозволило лісу (особливо старшим деревам) розвиватися краще.

2) критерій видалення ведмедя

коефіцієнт, який визначає мінімальний відсоток повалених лісорубів для відправлення ведмедя в зоопарк. Встановіть 0, щоб повернутися до початкового визначення, але це різке усунення ведмедя в основному обмежить популяцію до циклу коливань 0-1. Я встановив його на .15 (тобто ведмедя вивозять лише у тому випадку, якщо цього року 15% або більше дроворубів було забито). Це дозволяє помірній популяції ведмедів, достатній для запобігання очищенню ділянок дерев чистим районом, але все ж дозволяє зрубати значну частину лісу.

Як зауваження, моделювання ніколи не припиняється (навіть минулих 400 років). Це легко можна зробити, але це не так.

Кодекс

Код повністю міститься в одній HTML-сторінці.
Він повинен бути закодований UTF-8 для відображення належних символів unicode для ведмедів та дроворубів.

Для систем з обмеженими можливостями Unicode (наприклад, Ubuntu): знайдіть наступні рядки:

    jack   :{ pic: '🙎', color:'#bc0e11' },
    bear   :{ pic: '🐻', color:'#422f1e' }},

і змінити піктограми для символів легше дисплея ( #, *, що завгодно)

<!doctype html>
<meta charset=utf-8>
<title>Of jacks and bears</title>
<body onload='init();'>
    <style>
    #log p { margin-top: 0; margin-bottom: 0; }
    </style>
    <div id='main'>

    </div>
    <table>
        <tr>
            <td><canvas id='forest'></canvas></td>
            <td>
                <table>
                    <tr>
                        <td colspan=2>
                            <div>Forest size     <input type='text' size=10 onchange='create_forest(this.value);'>     </div>
                            <div>Simulation tick <input type='text' size= 5 onchange='set_tick(this.value);'     > (ms)</div>
                            <div>
                                <input type='button' value='◾'       onclick='stop();'>
                                <input type='button' value='▸'       onclick='start();'>
                                <input type='button' value='1 month' onclick='start(1);'>
                                <input type='button' value='1 year'  onclick='start(12);'>
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td id='log' colspan=2>
                        </td>
                    </tr>
                    <tr>
                        <td><canvas id='graphs'></canvas></td>
                        <td id='legend'></td>
                    </tr>
                    <tr>
                        <td align='center'>evolution over 60 years</td>
                        <td id='counters'></td>
                    </tr>
                </table>
            </td>
        </tr>
    </table>
<script>
// ==================================================================================================
// Global parameters
// ==================================================================================================

var Prm = {
    // ------------------------------------
    // as defined in the original challenge
    // ------------------------------------

    // forest size
    forest_size: 45, // 2025 cells

    // simulation duration
    duration: 400*12, // 400 years

    // initial populations
    populate: { trees: .5, jacks:.1, bears:.02 },

    // tree ages
    age: { mature:12, elder:120 },

    // tree spawning probabilities
    spawn: { sapling:0, mature:.1, elder:.2 },

    // tree lumber yields
    lumber: { mature:1, elder:2 },

    // walking distances
    distance: { jack:3, bear:5 },

    // ------------------------------------
    // extra tweaks
    // ------------------------------------

    // lumberjacks growth rate
    // (set to 1 in original contest parameters)
    jacks_growth: 1, // .5,

    // minimal fraction of lumberjacks mauled to send a bear to the zoo
    // (set to 0 in original contest parameters)
    mauling_threshold: .15, // 0,

    // ------------------------------------
    // internal helpers
    // ------------------------------------

    // offsets to neighbouring cells
    neighbours: [ 
    {x:-1, y:-1}, {x: 0, y:-1}, {x: 1, y:-1},
    {x:-1, y: 0},               {x: 1, y: 0},
    {x:-1, y: 1}, {x: 0, y: 1}, {x: 1, y: 1}],

    // ------------------------------------
    // goodies
    // ------------------------------------

    // bear and people names
    names: 
    { bear: ["Art", "Ursula", "Arthur", "Barney", "Bernard", "Bernie", "Bjorn", "Orson", "Osborn", "Torben", "Bernadette", "Nita", "Uschi"],
     jack: ["Bob", "Tom", "Jack", "Fred", "Paul", "Abe", "Roy", "Chuck", "Rob", "Alf", "Tim", "Tex", "Mel", "Chris", "Dave", "Elmer", "Ian", "Kyle", "Leroy", "Matt", "Nick", "Olson", "Sam"] },

    // months
    month: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ],

    // ------------------------------------
    // graphics
    // ------------------------------------

    // messages verbosity (set to 2 to be flooded, -1 to have no trace at all)
    verbosity: 1,

     // pixel sizes
     icon_size: 100,
     canvas_f_size: 600,   // forest canvas size
     canvas_g_width : 400, // graphs canvas size
     canvas_g_height: 200,

     // graphical representation
     graph: { 
        soil: { color: '#82641e' },
        sapling:{ radius:.1, color:'#52e311', next:'mature'},
        mature :{ radius:.3, color:'#48b717', next:'elder' },
        elder  :{ radius:.5, color:'#8cb717', next:'elder' },
        jack   :{ pic: '🙎', color:'#2244ff' },
        bear   :{ pic: '🐻', color:'#422f1e' },
        mauling:{ pic: '★', color:'#ff1111' },
        cutting:{ pic: '●', color:'#441111' }},

    // animation tick
    tick:100 // ms
};

// ==================================================================================================
// Utilities
// ==================================================================================================

function int_rand (num)
{
    return Math.floor (Math.random() * num);
}

function shuffle (arr)
{
    for (
        var j, x, i = arr.length;
        i; 
        j = int_rand (i), x = arr[--i], arr[i] = arr[j], arr[j] = x);
}

function pick (arr)
{
    return arr[int_rand(arr.length)];
}

function message (str, level)
{
    level = level || 0;
    if (level <= Prm.verbosity)
    {
        while (Gg.log.childNodes.length > 10) Gg.log.removeChild(Gg.log.childNodes[0]);
        var line = document.createElement ('p');
        line.innerHTML = Prm.month[Forest.date%12]+" "+Math.floor(Forest.date/12)+": "+str;
        Gg.log.appendChild (line);
    }
}

// ==================================================================================================
// Forest
// ==================================================================================================

// --------------------------------------------------------------------------------------------------
// a forest cell
// --------------------------------------------------------------------------------------------------
function cell()
{
    this.contents = [];
}

cell.prototype = {

    add: function (elt)
    {
        this.contents.push (elt);
    },

    remove: function (elt)
    {
        var i = this.contents.indexOf (elt);
        this.contents.splice (i, 1);
    },

    contains: function (type)
    {
        for (var i = 0 ; i != this.contents.length ; i++)
        {
            if (this.contents[i].type == type)
            {
                return this.contents[i];
            }
        }
        return null;
    }
}

// --------------------------------------------------------------------------------------------------
// an entity (tree, jack, bear)
// --------------------------------------------------------------------------------------------------
function entity (x, y, type)
{
    this.age = 0;
    switch (type)
    {
        case "jack": this.name = pick (Prm.names.jack); break;
        case "bear": this.name = pick (Prm.names.bear); break;
        case "tree": this.name = "sapling"; Forest.t.low++; break;
    }

    this.x = this.old_x = x;
    this.y = this.old_y = y;
    this.type = type;
}

entity.prototype = {
    move: function ()
    {
        Forest.remove (this);
        var n = neighbours (this);
        this.x = n[0].x;
        this.y = n[0].y;
        return Forest.add (this);
    }
};

// --------------------------------------------------------------------------------------------------
// a list of entities (trees, jacks, bears)
// --------------------------------------------------------------------------------------------------
function elt_list (type)
{
    this.type = type;
    this.list = [];
}

elt_list.prototype = {
    add: function (x, y)
    {
        if (x === undefined) x = int_rand (Forest.size);
        if (y === undefined) y = int_rand (Forest.size);
        var e = new entity (x, y, this.type);
        Forest.add (e);
        this.list.push (e);
        return e;
    },

    remove: function (elt)
    {
        var i;
        if (elt) // remove a specific element (e.g. a mauled lumberjack)
        {
            i = this.list.indexOf (elt);
        }
        else // pick a random element (e.g. a bear punished for the collective pancake rampage)
        {           
            i = int_rand(this.list.length);
            elt = this.list[i];
        }
        this.list.splice (i, 1);
        Forest.remove (elt);
        if (elt.name == "mature") Forest.t.mid--;
        if (elt.name == "elder" ) Forest.t.old--;
        return elt;
    }
};

// --------------------------------------------------------------------------------------------------
// global forest handling
// --------------------------------------------------------------------------------------------------
function forest (size)
{
    // initial parameters
    this.size = size;
    this.surface = size * size;
    this.date = 0;
    this.mauling = this.lumber = 0;
    this.t = { low:0, mid:0, old:0 };

    // initialize cells
    this.cells = new Array (size);
    for (var i = 0 ; i != size ; i++)
    {
        this.cells[i] = new Array(size);
        for (var j = 0 ; j != size ; j++)
        {
            this.cells[i][j] = new cell;
        }
    }

    // initialize entities lists
    this.trees = new elt_list ("tree");
    this.jacks = new elt_list ("jack");
    this.bears = new elt_list ("bear");
    this.events = [];
}

forest.prototype = {
    populate: function ()
    {
        function fill (num, list)
        {
            for (var i = 0 ; i < num ; i++)
            {
                var coords = pick[i_pick++];
                list.add (coords.x, coords.y);
            }
        }

        // shuffle forest cells
        var pick = new Array (this.surface);
        for (var i = 0 ; i != this.surface ; i++)
        {
            pick[i] = { x:i%this.size, y:Math.floor(i/this.size)};
        }
        shuffle (pick);
        var i_pick = 0;

        // populate the lists
        fill (Prm.populate.jacks * this.surface, this.jacks);
        fill (Prm.populate.bears * this.surface, this.bears);
        fill (Prm.populate.trees * this.surface, this.trees);
        this.trees.list.forEach (function (elt) { elt.age = Prm.age.mature; });
    },

    add: function (elt)
    {
        var cell = this.cells[elt.x][elt.y];
        cell.add (elt);
        return cell;
    },

    remove: function (elt)
    {
        var cell = this.cells[elt.x][elt.y];
        cell.remove (elt);
    },

    evt_mauling: function (jack, bear)
    {
        message (bear.name+" sniffs a delicious scent of pancake, unfortunately for "+jack.name, 1);
        this.jacks.remove (jack);
        this.mauling++;
        Gg.counter.mauling.innerHTML = this.mauling;
        this.register_event ("mauling", jack);
    },

    evt_cutting: function (jack, tree)
    {
        if (tree.name == 'sapling') return; // too young to be chopped down
        message (jack.name+" cuts a "+tree.name+" tree: lumber "+this.lumber+" (+"+Prm.lumber[tree.name]+")", 2);
        this.trees.remove (tree);
        this.lumber += Prm.lumber[tree.name];
        Gg.counter.cutting.innerHTML = this.lumber;
        this.register_event ("cutting", jack);
    },

    register_event: function (type, position)
    {
        this.events.push ({ type:type, x:position.x, y:position.y});
    },

    tick: function()
    {
        this.date++;
        this.events = [];

        // monthly updates
        this.trees.list.forEach (b_tree);
        this.jacks.list.forEach (b_jack);
        this.bears.list.forEach (b_bear);

        // feed graphics
        Gg.graphs.trees.add (this.trees.list.length);
        Gg.graphs.jacks.add (this.jacks.list.length);
        Gg.graphs.bears.add (this.bears.list.length);
        Gg.graphs.sapling.add (this.t.low);
        Gg.graphs.mature .add (this.t.mid);
        Gg.graphs.elder  .add (this.t.old);

        // yearly updates
        if (!(this.date % 12))
        {
            // update jacks
            if (this.jacks.list.length == 0)
            {
                message ("An extra lumberjack is hired after a bear rampage");
                this.jacks.add ();
            }

            if (this.lumber >= this.jacks.list.length)
            {
                var extra_jacks = Math.floor (this.lumber / this.jacks.list.length * Prm.jacks_growth);
                message ("A good lumbering year. Lumberjacks +"+extra_jacks, 1);
                for (var i = 0 ; i != extra_jacks ; i++) this.jacks.add ();
            }
            else if (this.jacks.list.length > 1)
            {
                var fired = this.jacks.remove();
                message (fired.name+" has been chopped", 1);
            }

            // update bears
            if (this.mauling > this.jacks.list.length * Prm.mauling_threshold)
            {
                var bear = this.bears.remove();
                message (bear.name+" will now eat pancakes in a zoo", 1);
            }
            else
            {
                var bear = this.bears.add();
                message (bear.name+" starts a quest for pancakes", 1);
            }

            // reset counters
            this.mauling = this.lumber = 0;
        }
    }

}

function neighbours (elt)
{
    var ofs,x,y;
    var list = [];
    for (ofs in Prm.neighbours)
    {
        var o = Prm.neighbours[ofs];
        x = elt.x + o.x;
        y = elt.y + o.y;
        if (  x < 0 || x >= Forest.size
           || y < 0 || y >= Forest.size) continue;

        list.push ({x:x, y:y});
    }
    shuffle (list);
    return list;
}

// --------------------------------------------------------------------------------------------------
// entities behaviour
// --------------------------------------------------------------------------------------------------
function b_tree (tree)
{
    // update tree age and category
    if      (tree.age == Prm.age.mature) { tree.name = "mature"; Forest.t.low--; Forest.t.mid++; }
    else if (tree.age == Prm.age.elder ) { tree.name = "elder" ; Forest.t.mid--; Forest.t.old++; }
    tree.age++;

    // see if we can spawn something
    if (Math.random() < Prm.spawn[tree.name])
    {
        var n = neighbours (tree);
        for (var i = 0 ; i != n.length ; i++)
        {
            var coords = n[i];
            var cell = Forest.cells[coords.x][coords.y];
            if (cell.contains("tree")) continue;
            Forest.trees.add (coords.x, coords.y);
            break;
        }
    }
}

function b_jack (jack)
{
    jack.old_x = jack.x;
    jack.old_y = jack.y;

    for (var i = 0 ; i != Prm.distance.jack ; i++)
    {
        // move
        var cell = jack.move ();

        // see if we stumbled upon a bear
        var bear = cell.contains ("bear");
        if (bear)
        {
            Forest.evt_mauling (jack, bear);
            break;
        }

        // see if we reached an harvestable tree
        var tree = cell.contains ("tree");
        if (tree)
        {
            Forest.evt_cutting (jack, tree);
            break;
        }
    }
}

function b_bear (bear)
{
    bear.old_x = bear.x;
    bear.old_y = bear.y;

    for (var i = 0 ; i != Prm.distance.bear ; i++)
    {
        var cell = bear.move ();
        var jack = cell.contains ("jack");
        if (jack)
        {
            Forest.evt_mauling (jack, bear);
            break; // one pancake hunt per month is enough
        }
    }
}

// --------------------------------------------------------------------------------------------------
// Graphics
// --------------------------------------------------------------------------------------------------
function init()
{
    function create_counter (desc)
    {
        var counter = document.createElement ('span');
        var item = document.createElement ('p');
        item.innerHTML = desc.name+"&nbsp;";
        item.style.color = desc.color;
        item.appendChild (counter);
        return { item:item, counter:counter };
    }

    // initialize forest canvas
    Gf = { period:20, tick:0 };
    Gf.canvas = document.getElementById ('forest');
    Gf.canvas.width  =
    Gf.canvas.height = Prm.canvas_f_size;
    Gf.ctx = Gf.canvas.getContext ('2d');
    Gf.ctx.textBaseline = 'Top';

    // initialize graphs canvas
    Gg = { counter:[] };
    Gg.canvas = document.getElementById ('graphs');
    Gg.canvas.width  = Prm.canvas_g_width;
    Gg.canvas.height = Prm.canvas_g_height;
    Gg.ctx = Gg.canvas.getContext ('2d');

    // initialize graphs
    Gg.graphs = {
        jacks:   new graphic({ name:"lumberjacks" , color:Prm.graph.jack.color }),
        bears:   new graphic({ name:"bears"       , color:Prm.graph.bear.color, ref:'jacks' }),
        trees:   new graphic({ name:"trees"       , color:'#0F0' }),
        sapling: new graphic({ name:"saplings"    , color:Prm.graph.sapling.color, ref:'trees' }),
        mature:  new graphic({ name:"mature trees", color:Prm.graph.mature .color, ref:'trees' }),
        elder:   new graphic({ name:"elder trees" , color:Prm.graph.elder  .color, ref:'trees' })
    };
    Gg.legend = document.getElementById ('legend');
    for (g in Gg.graphs)
    {
        var gr = Gg.graphs[g];
        var c = create_counter (gr);
        gr.counter = c.counter;
        Gg.legend.appendChild (c.item);
    }

    // initialize counters
    var counters = document.getElementById ('counters');
    var def = [ "mauling", "cutting" ];
    var d; for (d in def)
    {
        var c = create_counter ({ name:def[d], color:Prm.graph[def[d]].color });
        counters.appendChild (c.item);
        Gg.counter[def[d]] = c.counter;
    }

    // initialize log
    Gg.log = document.getElementById ('log');

    // create our forest
    create_forest(Prm.forest_size);
    start();
}

function create_forest (size)
{
    if (size < 2) size = 2;
    if (size > 100) size = 100;
    Forest = new forest (size);
    Prm.icon_size = Prm.canvas_f_size / size;
    Gf.ctx.font = 'Bold '+Prm.icon_size+'px Arial';
    Forest.populate ();
    draw_forest();
    var g; for (g in Gg.graphs) Gg.graphs[g].reset();
    draw_graphs();
}

function animate()
{
    if (Gf.tick % Prm.tick == 0)
    {
        Forest.tick();
        draw_graphs();
    }
    draw_forest();
    Gf.tick+= Gf.period;
    if (Gf.tick == Gf.stop_date) stop();
}

function draw_forest ()
{
    function draw_dweller (dweller)
    {
        var type = Prm.graph[dweller.type];
        Gf.ctx.fillStyle = type.color;
        var x = dweller.x * time_fraction + dweller.old_x * (1 - time_fraction);
        var y = dweller.y * time_fraction + dweller.old_y * (1 - time_fraction);
        Gf.ctx.fillText (type.pic, x * Prm.icon_size, (y+1) * Prm.icon_size);
    }

    function draw_event (evt)
    {
        var gr = Prm.graph[evt.type];
        Gf.ctx.fillStyle = gr.color;
        Gf.ctx.fillText (gr.pic, evt.x * Prm.icon_size, (evt.y+1) * Prm.icon_size);
    }

    function draw_tree (tree)
    {
        // trees grow from one category to the next
        var type = Prm.graph[tree.name];
        var next = Prm.graph[type.next];
        var radius = (type.radius + (next.radius - type.radius) / Prm.age[type.next] * tree.age) * Prm.icon_size;
        Gf.ctx.fillStyle = Prm.graph[tree.name].color;
        Gf.ctx.beginPath();
        Gf.ctx.arc((tree.x+.5) * Prm.icon_size, (tree.y+.5) * Prm.icon_size, radius, 0, 2*Math.PI);
        Gf.ctx.fill();
    }

    // background
    Gf.ctx.fillStyle = Prm.graph.soil.color;
    Gf.ctx.fillRect (0, 0, Gf.canvas.width, Gf.canvas.height);

    // time fraction to animate displacements
    var time_fraction = (Gf.tick % Prm.tick) / (Prm.tick-Gf.period);

    // entities
    Forest.trees.list.forEach (draw_tree);
    Forest.jacks.list.forEach (draw_dweller);
    Forest.bears.list.forEach (draw_dweller);
    Forest.events.forEach (draw_event);
}

// --------------------------------------------------------------------------------------------------
// Graphs
// --------------------------------------------------------------------------------------------------
function graphic (prm)
{
    this.name  = prm.name  || '?';
    this.color = prm.color || '#FFF';
    this.size  = prm.size  || 720;
    this.ref   = prm.ref;
    this.values = [];
    this.counter = document.getElement
}

graphic.prototype = {
    draw: function ()
    {
        Gg.ctx.strokeStyle = this.color;
        Gg.ctx.beginPath();
        for (var i = 0 ; i != this.values.length ; i++)
        {
            var x = (i + this.size - this.values.length) / this.size * Gg.canvas.width;
            var y = (1-(this.values[i] - this.min) / this.rng)       * Gg.canvas.height;

            if (i == 0) Gg.ctx.moveTo (x, y);
            else        Gg.ctx.lineTo (x, y);
        }
        Gg.ctx.stroke();
    },

    add: function (value)
    {
        // store value
        this.values.push (value);
        this.counter.innerHTML = value;

        // cleanup history
        while (this.values.length > this.size) this.values.splice (0,1);

        // compute min and max
        this.min = Math.min.apply(Math, this.values);
        if (this.min > 0) this.min = 0;
        this.max = this.ref 
                 ? Gg.graphs[this.ref].max
                 : Math.max.apply(Math, this.values);
        this.rng = this.max - this.min;
        if (this.rng == 0) this.rng = 1;
    },

    reset: function()
    {
        this.values = [];
    }
}

function draw_graphs ()
{
    function draw_graph (graph)
    {
        graph.draw();
    }

    // background
    Gg.ctx.fillStyle = '#000';
    Gg.ctx.fillRect (0, 0, Gg.canvas.width, Gg.canvas.height);

    // graphs
    var g; for (g in Gg.graphs)
    {
        var gr = Gg.graphs[g];
        gr.draw();
    }
}

// --------------------------------------------------------------------------------------------------
// User interface
// --------------------------------------------------------------------------------------------------
function set_tick(value)
{
    value = Math.round (value / Gf.period);
    if (value < 2) value = 2;
    value *= Gf.period;
    Prm.tick = value;
    return value;
}

function start (duration)
{
    if (Prm.timer) stop();
    Gf.stop_date = duration ? Gf.tick + duration*Prm.tick : -1;
    Prm.timer = setInterval (animate, Gf.period);
}

function stop ()
{
    if (Prm.timer)
    {
        clearInterval (Prm.timer);
        Prm.timer = null;
    }
    Gf.stop_date = -1;
}

</script>
</body>

Що далі?

Ще зауваження все ще вітаються.

NB: Я знаю, що кількість саджанців / зрілих / старих дерев все ще дещо безладна, але до біса.

Крім того, я вважаю, що document.getElementById читабельніше, ніж $, тому не потрібно скаржитися на відсутність jQueryisms. Це спеціально безкоштовний jQuery. Кожному своє, правда?


+1, це виглядає погано, я хотів би бачити інтерактивність!
Вільям Барбоса

Я не можу бачити ведмедів і дроворубів. Що мені робити, щоб їх побачити? Btw, тільки максимум одного ведмедя потрібно видаляти на рік, правда?
justhalf

@WilliamBarbosa Якщо vox populi кричить досить голосно, це може змусити мене додати трохи :)

@justhalf дивись мою редакцію про символи Unicode. Що стосується ведмедів, якщо я правильно розумію специфікації, так: лише один ведмідь, зібраний навмання, щороку відправляється в зоопарк, якщо трапляються якісь малінг.

1
Зростаючі дерева дивовижні. Хороша робота!
Blackhole

14

КутовийJS

Ось моя версія , яка все ще працює в процесі: код трохи… ну… некрасиво. І досить повільно. Я також планую додати більше варіантів для параметризації еволюції та аналізу стану лісу. Зауваження та пропозиції щодо меліорації вітаються!

Скріншот лісу

Демонстрація

<div ng-app="ForestApp" ng-controller="ForestController">
    <form name="parametersForm" ng-hide="evolutionInProgress" autocomplete="off" novalidate>
        <div class="line">
            <label for="forestSize">Size of the forest:</label>
            <input type="number" ng-model="Parameters.forestSize" id="forestSize" min="5" ng-pattern="/^[0-9]+$/" required />
        </div>
        <div class="line">
            <label for="simulationInterval">Number of milliseconds between each tick</label>
            <input type="number" ng-model="Parameters.simulationInterval" id="simulationInterval" min="10" ng-pattern="/^[0-9]+$/" required />
        </div>
        <div class="line">
            <label for="animationsEnabled">Animations enabled?
                <br /><small>(>= 300 ms between each tick is advisable)</small>

            </label>
            <input type="checkbox" ng-model="Parameters.animationsEnabled" id="animationsEnabled" />
        </div>
        <div class="line">
            <button ng-disabled="parametersForm.$invalid || evolutionInProgress" ng-click="launchEvolution()">Launch the evolution!</button>
        </div>
    </form>
    <div id="forest" ng-style="{width: side = (20*Parameters.forestSize) + 'px', height: side, 'transition-duration': transitionDuration = (Parameters.animationsEnabled ? 0.8*Parameters.simulationInterval : 0) + 'ms', '-webkit-transition-duration': transitionDuration}">
        <div ng-repeat="bear in Forest.bearsList" class="entity entity--bear" ng-style="{left: (20*bear.x) + 'px', top: (20*bear.y) + 'px'}"></div>
        <div ng-repeat="lumberjack in Forest.lumberjacksList" class="entity entity--lumberjack" ng-style="{left: (20*lumberjack.x) + 'px', top: (20*lumberjack.y) + 'px'}"></div>
        <div ng-repeat="tree in Forest.treesList" class="entity entity--tree" ng-class="'entity--tree--' + tree.stage" ng-style="{left: (20*tree.x) + 'px', top: (20*tree.y) + 'px'}"></div>
    </div>
    <div class="line"><em>Age of the forest:</em><samp>{{floor(Forest.age/12)}} year{{floor(Forest.age/12) > 1 ? 's' : ''}} and {{Forest.age%12}} month{{Forest.age%12 > 1 ? 's' : ''}}</samp></div>
    <div class="line"><em>Number of bears:</em><samp>{{Forest.bearsList.length}}</samp></div>
    <div class="line"><em>Number of lumberjacks:</em><samp>{{Forest.lumberjacksList.length}}</samp></div>
    <br />
    <div class="line"><em>Number of lumbers collected:</em><samp>{{Forest.numberOfLumbers}}</samp></div>
    <div class="line"><em>Number of mauls:</em><samp>{{Forest.numberOfMauls}}</samp></div>
</div>
/** @link http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array */
function shuffle(array) {
    var currentIndex = array.length,
        temporaryValue, randomIndex;

    // While there remain elements to shuffle...
    while (0 !== currentIndex) {

        // Pick a remaining element...
        randomIndex = Math.floor(Math.random() * currentIndex);
        currentIndex -= 1;

        // And swap it with the current element.
        temporaryValue = array[currentIndex];
        array[currentIndex] = array[randomIndex];
        array[randomIndex] = temporaryValue;
    }

    return array;
}

var forestApp = angular.module('ForestApp', ['ngAnimate']);

forestApp.value('Parameters', {
    /** @var int[] Maximal number of moves by species */
    speed: {
        bear: 5,
        lumberjack: 3,
    },

    /** @var int[] Initial percentage of each species in the forest */
    initialPercentage: {
        bear: 2,
        lumberjack: 10,
        tree: 50,
    },

    /** @var int[] Spawing rate, in percentage, of new saplings around an existing tree */
    spawningPercentage: {
        0: 0,
        1: 10,
        2: 20,
    },

    /** @var int[] Age of growth for an existing tree */
    ageOfGrowth: {
        sapling: 12,
        tree: 120,
    },

    /** @var int[] Lumber collected on an existing tree */
    numberOfLumbers: {
        tree: 1,
        elderTree: 2,
    },

    /** @var int Size of each side of the forest */
    forestSize: 20,

    /** @var int Number of milliseconds between each tick (month in the forest) */
    simulationInterval: 50,
});

forestApp.constant('TREE_STAGE', {
    SAPLING: 0,
    TREE: 1,
    ELDER_TREE: 2,
});

forestApp.factory('Tree', ['Forest', 'Parameters', 'TREE_STAGE', function (Forest, Parameters, TREE_STAGE) {
    // Classes which represents a tree
    var Tree = function (stage, x, y) {
        /** @var TREE_STAGE Current stage of the tree */
        this.stage = stage;

        /** @var int Current age of the tree, in month */
        this.age = 0;

        /** @var int X coordinates of the tree */
        this.x = x;

        /** @var int Y coordinates of the tree */
        this.y = y;

        this.tick = function () {
            if (Math.random() < Parameters.spawningPercentage[this.stage] / 100) {
                var freePositionsList = shuffle(Forest.getFreePositionsAround(this.x, this.y));
                if (freePositionsList.length > 0) {
                    var saplingPosition = freePositionsList[0];

                    Tree.create(TREE_STAGE.SAPLING, saplingPosition[0], saplingPosition[1]);
                }
            }

            ++this.age;

            if (this.stage === TREE_STAGE.SAPLING && this.age == Parameters.ageOfGrowth.sapling) {
                this.stage = TREE_STAGE.TREE;
            } else if (this.stage === TREE_STAGE.TREE && this.age == Parameters.ageOfGrowth.tree) {
                this.stage = TREE_STAGE.ELDER_TREE;
            }
        };

        /**
         * Remove the entity
         */
        this.remove = function () {
            var index = Forest.treesList.indexOf(this);
            Forest.treesList.splice(index, 1);
        };
    };

    Tree.create = function (stage, x, y) {
        Forest.add.tree(new Tree(stage, x, y));
    };

    return Tree;
}]);

forestApp.factory('Lumberjack', ['Forest', 'Parameters', 'TREE_STAGE', function (Forest, Parameters, TREE_STAGE) {
    // Classes which represents a lumberjack
    var Lumberjack = function (x, y) {
        /** @var int X coordinates of the lumberjack */
        this.x = x;

        /** @var int Y coordinates of the lumberjack */
        this.y = y;

        this.tick = function () {
            for (movement = Parameters.speed.lumberjack; movement > 0; --movement) {
                var positionsList = shuffle(Forest.getPositionsAround(this.x, this.y));
                var newPosition = positionsList[0];
                this.x = newPosition[0];
                this.y = newPosition[1];

                var tree = Forest.getTreeAt(this.x, this.y);
                if (tree !== null) {
                    if (tree.stage === TREE_STAGE.SAPLING) {
                        return;
                    } else if (tree.stage === TREE_STAGE.TREE) {
                        Forest.numberOfLumbers += Parameters.numberOfLumbers.tree;
                    } else {
                        Forest.numberOfLumbers += Parameters.numberOfLumbers.elderTree;
                    }

                    tree.remove();
                    movement = 0;
                };
            }
        };

        /**
         * Remove the entity
         */
        this.remove = function () {
            if (Forest.lumberjacksList.length === 1) {
                this.x = Math.floor(Math.random() * Parameters.forestSize);
                this.y = Math.floor(Math.random() * Parameters.forestSize);
            } else {
                var index = Forest.lumberjacksList.indexOf(this);
                Forest.lumberjacksList.splice(index, 1);
            }
        };
    };

    Lumberjack.create = function (x, y) {
        Forest.add.lumberjack(new Lumberjack(x, y));
    };

    return Lumberjack;
}]);

forestApp.factory('Bear', ['Forest', 'Parameters', function (Forest, Parameters) {
    // Classes which represents a bear
    var Bear = function (x, y) {
        /** @var int X coordinates of the bear */
        this.x = x;

        /** @var int Y coordinates of the bear */
        this.y = y;

        this.tick = function () {
            for (movement = Parameters.speed.bear; movement > 0; --movement) {
                var positionsList = shuffle(Forest.getPositionsAround(this.x, this.y));
                var newPosition = positionsList[0];
                this.x = newPosition[0];
                this.y = newPosition[1];

                angular.forEach(Forest.getLumberjacksListAt(this.x, this.y), function (lumberjack) {
                    lumberjack.remove();
                    ++Forest.numberOfMauls;
                    movement = 0;
                });
            }
        };

        /**
         * Remove the entity
         */
        this.remove = function () {
            var index = Forest.bearsList.indexOf(this);
            Forest.bearsList.splice(index, 1);
        };
    };

    Bear.create = function (x, y) {
        Forest.add.bear(new Bear(x, y));
    };

    return Bear;
}]);

forestApp.service('Forest', ['Parameters', function (Parameters) {
    var forest = this;

    this.age = 0;
    this.numberOfLumbers = 0;
    this.numberOfMauls = 0;

    this.bearsList = [];
    this.lumberjacksList = [];
    this.treesList = [];

    this.getEntitiesList = function () {
        return forest.bearsList.concat(forest.lumberjacksList, forest.treesList);
    };

    /**
     * Age the forest by one month
     */
    this.tick = function () {
        angular.forEach(forest.getEntitiesList(), function (entity) {
            entity.tick();
        });

        ++forest.age;
    };

    this.add = {
        bear: function (bear) {
            forest.bearsList.push(bear);
        },
        lumberjack: function (lumberjack) {
            forest.lumberjacksList.push(lumberjack);
        },
        tree: function (tree) {
            forest.treesList.push(tree);
        },
    };

    /**
     * @return Tree|null Tree at this position, or NULL if there is no tree.
     */
    this.getTreeAt = function (x, y) {
        var numberOfTrees = forest.treesList.length;
        for (treeId = 0; treeId < numberOfTrees; ++treeId) {
            var tree = forest.treesList[treeId];
            if (tree.x === x && tree.y === y) {
                return tree;
            }
        }

        return null;
    };

    /**
     * @return Lumberjack[] List of the lumberjacks at this position
     */
    this.getLumberjacksListAt = function (x, y) {
        var lumberjacksList = [];
        angular.forEach(forest.lumberjacksList, function (lumberjack) {
            if (lumberjack.x === x && lumberjack.y === y) {
                lumberjacksList.push(lumberjack);
            }
        });

        return lumberjacksList;
    };

    /**
     * @return int[] Positions around this position
     */
    this.getPositionsAround = function (x, y) {
        var positionsList = [
            [x - 1, y - 1],
            [x, y - 1],
            [x + 1, y - 1],
            [x - 1, y],
            [x + 1, y],
            [x - 1, y + 1],
            [x, y + 1],
            [x + 1, y + 1]
        ];

        return positionsList.filter(function (position) {
            return (position[0] >= 0 && position[1] >= 0 && position[0] < Parameters.forestSize && position[1] < Parameters.forestSize);
        });
    };

    /**
     * @return int[] Positions without tree around this position
     */
    this.getFreePositionsAround = function (x, y) {
        var positionsList = forest.getPositionsAround(x, y);

        return positionsList.filter(function (position) {
            return forest.getTreeAt(position[0], position[1]) === null;
        });
    };
}]);

forestApp.controller('ForestController', ['$interval', '$scope', 'Bear', 'Forest', 'Lumberjack', 'Parameters', 'Tree', 'TREE_STAGE', function ($interval, $scope, Bear, Forest, Lumberjack, Parameters, Tree, TREE_STAGE) {
    $scope.Forest = Forest;
    $scope.Parameters = Parameters;
    $scope.evolutionInProgress = false;
    $scope.floor = Math.floor;

    var positionsList = [];

    /**
     * Start the evolution of the forest
     */
    $scope.launchEvolution = function () {
        $scope.evolutionInProgress = true;

        for (var x = 0; x < Parameters.forestSize; ++x) {
            for (var y = 0; y < Parameters.forestSize; ++y) {
                positionsList.push([x, y]);
            }
        }

        shuffle(positionsList);
        var numberOfBears = Parameters.initialPercentage.bear * Math.pow(Parameters.forestSize, 2) / 100;
        for (var bearId = 0; bearId < numberOfBears; ++bearId) {
            Bear.create(positionsList[bearId][0], positionsList[bearId][1]);
        }

        shuffle(positionsList);
        var numberOfLumberjacks = Parameters.initialPercentage.lumberjack * Math.pow(Parameters.forestSize, 2) / 100;
        for (var lumberjackId = 0; lumberjackId < numberOfLumberjacks; ++lumberjackId) {
            Lumberjack.create(positionsList[lumberjackId][0], positionsList[lumberjackId][1]);
        }

        shuffle(positionsList);
        var numberOfTrees = Parameters.initialPercentage.tree * Math.pow(Parameters.forestSize, 2) / 100;
        for (var treeId = 0; treeId < numberOfTrees; ++treeId) {
            Tree.create(TREE_STAGE.TREE, positionsList[treeId][0], positionsList[treeId][1]);
        }

        $interval(function () {
            Forest.tick();

            if (Forest.age % 12 === 0) {
                // Hire or fire lumberjacks
                if (Forest.numberOfLumbers >= Forest.lumberjacksList.length) {
                    shuffle(positionsList);
                    var numberOfLumberjacks = Math.floor(Forest.numberOfLumbers / Forest.lumberjacksList.length);
                    for (var lumberjackId = 0; lumberjackId < numberOfLumberjacks; ++lumberjackId) {
                        Lumberjack.create(positionsList[lumberjackId][0], positionsList[lumberjackId][1]);
                    }
                } else {
                    shuffle(Forest.lumberjacksList);
                    Forest.lumberjacksList[0].remove();
                }

                // Hire or fire bears
                if (Forest.numberOfMauls === 0) {
                    shuffle(positionsList);
                    Bear.create(positionsList[0][0], positionsList[0][1]);
                } else {
                    Forest.bearsList[0].remove();
                }

                Forest.numberOfLumbers = 0;
                Forest.numberOfMauls = 0;
            }

        }, Parameters.simulationInterval);
    };
}]);

1
також мені подобається, як ми використовуємо один і той же код для рандомізації масиву перетасовок
Kevin L

Цікаво. Перший раз, коли я запустив ваше моделювання, воно закінчується усіма ведмедями та відсутніми дроворубами, що змусило мене думати, що це моделювання є незбалансованим. Але після цього це більше ніколи не повторюється, тож я здогадуюсь, що всі дроворуби побиті ведмедями просто «невдача» (адже якщо ведмедів так багато, лесоруби майже напевно помруть)
justhalf

getEntitiesAtздається, процесорний свинар! запуск системи з сіткою 50x50 займає на моєму ПК більше однієї секунди на місяць. Також є випадок, коли всі дерева рубаються, тоді всі лісоруби обстрілюються, а карта повільно наповнюється ведмедями :). Спробуйте невеликий розмір (10 або менше), щоб побачити це.

@kuroineko: Це тому, що є помилка (а точніше - не виконане правило), коли після того, як дроворуб буде пошкоджений, і якщо він перестане на ведмедя, його там не буде. Так він доходить до 0. Виправте це за допомогою чека після Forest.tick(), якщо Forest.lumberjackList.length == 0, тоді Lumberjack.create(<number>, <number>).
justhalf

1
Я спробував налаштувати ваш код, щоб ведмеді вважали за краще залишатися в лісі (а також включати @ kuroineko-подібні критерії видалення ведмедів, наприклад, додайте ведмедів, якщо менше 10% маулінгу, і видаліть ведмедя, якщо більше 15% маулінгу ). Цікаво подивитися, як змінюється поведінка =)
justhalf

3

Javascript

Я думаю, що це здебільшого працює. Існує деяка хитра поведінка, де я породив усіх нових ведмедів / дроворубів синхронізовано і прямо поруч один з одним через лінь у вставках.

Ця реалізація не дозволяє лісорубам стояти на саджанцях, тому що ви знаєте, витоптування саджанців погано. Мистецтво Fiddle використовує кольорові прямокутники за замовчуванням, змініть другий рядок на false, щоб використовувати букви для малювання.

скрипка

HTML:

<canvas id="c" width="1" height="1"></canvas>
<div id="p1"></div>
<div id="p2"></div>
<div id="p3"></div>
<div id="p4"></div>

Js:

var n = 10; // Size of the grid
var drawUsingColor = true; // If true, draws colors for each entity instead :D
var intervalTime = 1000; // how often each tick happens, in milliseconds
var jackRatio = 0.1;
var treeRatio = 0.5;
var bearRatio = 0.02;
var size = 48; // Pixels allocated (in size x size) for each entity
var font = "30px Lucida Console"; // if drawUsingColor is false

var bearColor = '#8B4513'; // Saddlebrown
var elderColor = '#556B2F'; // DarkOliveGreen
var lumberjackColor = '#B22222'; // Firebrick
var treeColor = '#008000'; // Green
var saplingColor = '#ADFF2F'; // GreenYellow

// Game rules:
var spawnSaplingChance = 0.1;
var elderTreeAge = 120;
var elderSaplingChance = 0.2;
var treeAge = 12;
var lumberjackRange = 3;
var bearRange = 5;
var zooPeriod = 12; // If a maul happens within this period
var lumberPeriod = 12; // New lumberjacks hired in this period


var time = 1;

var world;
var n2 = n * n; //because one saved keystroke
var zooqueue = [];
var lumberqueue = [];
var canvas = document.getElementById('c'); // Needs more jquery
var context = canvas.getContext('2d');
context.font = font;

// various statistics
var treesAlive = 0;
var jacksAlive = 0;
var bearsAlive = 0;
var currentLumber = 0;
var lumberjacksMauled = 0;
var recentEvents = '';

// Entity is a bear, eldertree, lumberjack, tree, sapling, with age. aka belts.
function Entity(belts, birthday) {
    this.type = belts;
    this.age = 0;
    this.birthday = birthday;
}

function initWorld() {
    canvas.height = size * n;
    canvas.width = size * n;
    world = new Array(n2);

    // One pass spawning algorithm: numEntity = number of entity left to spawn
    // If rand() in range [0,numtrees), spawn tree
    // if rand() in range [numtrees, numtrees+numjacks), spawn lumberjack
    // if rand() in range [numtrees+numjacks, numtrees+numjacks+numbears), spawn bear

    var numTrees = treeRatio * n2;
    var numJacks = jackRatio * n2;
    var numBears = bearRatio * n2;

    var godseed = new Array(n2);
    for (var i = 0; i < n2; i++) {
        godseed[i] = i;
    }
    shuffle(godseed);

    for (var i = 0; i < n2; i++) {
        var god = godseed.pop();
        if (god < numTrees) {
            world[i] = new Entity('T', 0);
            treesAlive++;
        } else if (god < numTrees + numJacks) {
            world[i] = new Entity('L', 0);
            jacksAlive++;
        } else if (god < numTrees + numJacks + numBears) {
            world[i] = new Entity('B', 0);
            bearsAlive++;
        }
        // console.log(world, i);
    }

    // populate zoo array, lumber array
    for (var i = 0; i < zooPeriod; i++) {
        zooqueue.push(0);
    }

    for (var i = 0; i < lumberPeriod; i++) {
        lumberqueue.push(0);
    }
}

animateWorld = function () {
    recentEvents = '';
    computeWorld();
    drawWorld();
    time++;

    $('#p1').text(treesAlive + ' trees alive');
    $('#p2').text(bearsAlive + ' bears alive');
    $('#p3').text(jacksAlive + ' lumberjacks alive');
    $('#p4').text(recentEvents);
};

function computeWorld() {
    zooqueue.push(lumberjacksMauled);
    lumberqueue.push(currentLumber);

    // Calculate entity positions
    for (var i = 0; i < n2; i++) {
        if (world[i]) {
            switch (world[i].type) {
                case 'B':
                    bearStuff(i);
                    break;
                case 'E':
                    elderStuff(i);
                    break;
                case 'L':
                    lumberjackStuff(i);
                    break;
                case 'T':
                    treeStuff(i);
                    break;
                case 'S':
                    saplingStuff(i);
                    break;
            }
        }
    }

    // Pop the # mauls from zooPeriod's ago, if lumberjacksMauled > oldmauls, then someone was eaten.
    var oldmauls = zooqueue.shift();
    if (time % zooPeriod === 0) {
        if (lumberjacksMauled > oldmauls) {
            if (remove('B') == 1) {
                bearsAlive--;
                recentEvents += 'Bear sent to zoo! ';
            }
        } else {
            bearsAlive++;
            spawn('B');
            recentEvents += 'New bear appeared! ';
        }
    }

    var oldLumber = lumberqueue.shift();
    if (time % lumberPeriod === 0) {
        // # lumberjack to hire
        var hire = Math.floor((currentLumber - oldLumber) / jacksAlive);
        if (hire > 0) {
            recentEvents += 'Lumber jack hired! (' + hire + ') ';
            while (hire > 0) {
                jacksAlive++;
                spawn('L');
                hire--;
            }
        } else {
            if (remove('L') == 1) {
                recentEvents += 'Lumber jack fired!  ';
                jacksAlive--;
            }
            else {
            }
        }
    }

    // Ensure > 1 lumberjack
    if (jacksAlive === 0) {
        jacksAlive++;
        spawn('L');
        recentEvent += 'Lumberjack spontaneously appeared';
    }
}

// Not the job of spawn/remove to keep track of whatever was spawned/removed
function spawn(type) {
    var index = findEmpty(type);
    if (index != -1) {
        world[index] = new Entity(type, time);
    }
    // recentEvents += 'Spawned a ' + type + '\n';
}

function remove(type) {
    var index = findByType(type);
    if (index != -1) {
        world[index] = null;
        return 1;
    }
    return -1;
    // recentEvents += 'Removed a ' + type + '\n';
}

// Searches in world for an entity with type=type. Currently implemented as
// linear scan, which isn't very random
function findByType(type) {
    for (var i = 0; i < n2; i++) {
        if (world[i] && world[i].type == type) return i;
    }
    return -1;
}

// Also linear scan 
function findEmpty(type) {
    for (var i = 0; i < n2; i++) {
        if (!world[i]) {
            return i;
        }
    }
    return -1;
}

function bearStuff(index) {
    if (world[index].birthday == time) {
        return;
    }

    // Wander around
    var tindex = index;
    for (var i = 0; i < lumberjackRange; i++) {
        var neighbors = get8Neighbor(tindex);

        var mov = neighbors[Math.floor(Math.random() * neighbors.length)];
        if (world[mov] && world[mov].type == 'L') {
            recentEvents += 'Bear (' + index % 10 + ',' + Math.floor(index / 10) + ') mauled a Lumberjack (' + mov % 10 + ',' + Math.floor(mov / 10) + ') !';
            lumberjacksMauled++;
            jacksAlive--;
            world[mov] = new Entity('B', time);
            world[mov].age = ++world[index].age;
            world[index] = null;
            return;
        }
        tindex = mov;
    }

    if (!world[tindex]) {
        world[tindex] = new Entity('B', time);
        world[tindex].age = ++world[index].age;
        world[index] = null;
    }
}

function elderStuff(index) {
    if (world[index].birthday == time) {
        return;
    }
    neighbors = get8Neighbor(index);

    // spawn saplings
    for (var i = 0; i < neighbors.length; i++) {
        if (!world[neighbors[i]]) {
            if (Math.random() < elderSaplingChance) {
                world[neighbors[i]] = new Entity('S', time);
                treesAlive++;
            }
        }
    }

    // become older
    world[index].age++;
}

function lumberjackStuff(index) {
    if (world[index].birthday == time) {
        return;
    }

    // Wander around
    var tindex = index;
    for (var i = 0; i < lumberjackRange; i++) {
        var neighbors = get8Neighbor(tindex);

        var mov = neighbors[Math.floor(Math.random() * neighbors.length)];
        if (world[mov] && (world[mov].type == 'T' || world[mov].type == 'E')) {
            world[mov].type == 'T' ? currentLumber++ : currentLumber += 2;
            treesAlive--;
            world[mov] = new Entity('L', time);
            world[mov].age = ++world[index].age;
            world[index] = null;
            return;
        }
        tindex = mov;
    }

    if (!world[tindex]) {
        world[tindex] = new Entity('L', time);
        world[tindex].age = ++world[index].age;
        world[index] = null;
    }
}

function treeStuff(index) {
    if (world[index].birthday == time) {
        return;
    }
    neighbors = get8Neighbor(index);

    // spawn saplings
    for (var i = 0; i < neighbors.length; i++) {
        if (!world[neighbors[i]]) {
            if (Math.random() < spawnSaplingChance) {
                world[neighbors[i]] = new Entity('S', time);
                treesAlive++;
            }
        }
    }

    // promote to elder tree?
    if (world[index].age >= elderTreeAge) {
        world[index] = new Entity('E', time);
        return;
    }

    // become older
    world[index].age++;
}

function saplingStuff(index) {
    if (world[index].birthday == time) {
        return;
    }

    // promote to tree?
    if (world[index].age > treeAge) {
        world[index] = new Entity('T', time);
        return;
    }
    world[index].age++;
}

// Returns array containing up to 8 valid neighbors.
// Prolly gonna break for n < 3 but oh well
function get8Neighbor(index) {
    neighbors = [];

    if (index % n != 0) {
        neighbors.push(index - n - 1);
        neighbors.push(index - 1);
        neighbors.push(index + n - 1);
    }
    if (index % n != n - 1) {
        neighbors.push(index - n + 1);
        neighbors.push(index + 1);
        neighbors.push(index + n + 1);
    }
    neighbors.push(index - n);
    neighbors.push(index + n);
    return neighbors.filter(function (val, ind, arr) {
        return (0 <= val && val < n2)
    });
}

// Each entity allocated 5x5px for their art
function drawWorld() {
    context.clearRect(0, 0, canvas.width, canvas.height);
    for (var i = 0; i < n2; i++) {
        if (world[i]) {
            var x = i % n;
            var y = Math.floor(i / n);
            switch (world[i].type) {
                case 'B':
                    drawBear(x, y);
                    break;
                case 'E':
                    drawElder(x, y);
                    break;
                case 'L':
                    drawJack(x, y);
                    break;
                case 'T':
                    drawTree(x, y);
                    break;
                case 'S':
                    drawSapling(x, y);
                    break;
            }
        }
    }
}

function drawBear(x, y) {
    if (drawUsingColor) {
        drawRect(x * size, y * size, size, size, bearColor);
    } else {
        drawLetter(x * size, y * size, 'B');
    }
}

function drawElder(x, y) {
    if (drawUsingColor) {
        drawRect(x * size, y * size, size, size, elderColor);
    } else {
        drawLetter(x * size, y * size, 'E');
    }
}

function drawJack(x, y) {
    if (drawUsingColor) {
        drawRect(x * size, y * size, size, size, lumberjackColor);
    } else {
        drawLetter(x * size, y * size, 'J');
    }
}

function drawTree(x, y) {
    if (drawUsingColor) {
        drawRect(x * size, y * size, size, size, treeColor);
    } else {
        drawLetter(x * size, y * size, 'T');
    }
}

function drawSapling(x, y) {
    if (drawUsingColor) {
        drawRect(x * size, y * size, size, size, saplingColor);
    } else {
        drawLetter(x * size, y * size, 'S');
    }
}

function drawLine(x1, y1, x2, y2, c) {
    context.beginPath();
    context.moveTo(x1, y1);
    context.lineTo(x2, y2);
    context.lineWidth = 3;
    context.strokeStyle = c;
    context.stroke();
}

function drawRect(x, y, w, h, c) {
    context.fillStyle = c;
    context.fillRect(x, y, w, h);
}

function drawLetter(x, y, l) {
    context.fillText(l, x, y);
}

$(document).ready(function () {
    initWorld();
    intervalID = window.setInterval(animateWorld, intervalTime);
    /*$('#s').click(function() {
        animateWorld();
    })*/
});

// http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
function shuffle(array) {
    var currentIndex = array.length,
        temporaryValue, randomIndex;

    // While there remain elements to shuffle...
    while (0 !== currentIndex) {

        // Pick a remaining element...
        randomIndex = Math.floor(Math.random() * currentIndex);
        currentIndex -= 1;

        // And swap it with the current element. 
        temporaryValue = array[currentIndex];
        array[currentIndex] = array[randomIndex];
        array[randomIndex] = temporaryValue;
    }
    return array;
}

З великим лісом є щось дивне (спробуйте n = 50, наприклад).
Blackhole

@Blackhole щось дивне = ____?
Кевін Л


Я помічаю, як ведмеді застрягають, і ліс переймає нижню частину карти з моменту нересту лісорубів у верхній частині карти
Кевін Л

1
Щось не так у тому, як нерести нових ведмедів та дроворубів. Я думаю, що введення цього пункту у верхній лівий кут трохи змінить глобальний баланс системи.

3

Пітон

Нічого фантазійного. Я продовжував додавати речі, тому рефакторинг може бути в порядку. (І я не робив об'єднання, щоб помилки все ще були присутні).

Я дав випадкові імена лісорубам та ведмедям. Дерева iто I, значить #, дроворуби є x, ведмеді єo

import os

from random import randint, choice, shuffle
from time import sleep

NGRID = 15
SLEEPTIME = 0.0125
DURATION = 4800
VERBOSE = True

###init
grid = [[[] for _ in range(NGRID)] for _ in range(NGRID)]
#Money earned this year
n_lumbers = 0
#Lumberjacks killed this year
n_maul = 0

tick = 0
events = []
#total number of
d_total = {'trees':0,
           'lumberjacks': 0,
           'bears': 0,
           'cut': 0,
           'maul': 0,
           'capture': 0,
           'lumbers': 0,
           'fired': 0}

d_oldest = {'tree': 0,
            'lumberjack': (0, ""),
            'bear': (0, "")}

d_most = {'n_maul': (0, ""),
          'n_lumber': (0, ""),
          'n_cut': (0, "")}

d_year = {'n_maul': 0,
          'n_lumber': 0}

###Classes
class Tree(object):
    """Represent a Sapling, Tree, or Elder Tree"""
    def __init__(self, coords, m=0, t='Sapling'):
        self.months = m
        self.typeof = t
        self.coords = coords
    def grow(self, m=1):
        """the tree grows 1 month and its type might change"""
        self.months = self.months + m
        if self.months == 12:
            self.typeof = 'Tree'
        elif self.months == 480:
            self.typeof = 'Elder Tree'
    def __str__(self):
        if self.typeof == 'Sapling':
            return 'i'
        elif self.typeof == 'Tree':
            return 'I'
        else:
            return '#'

class Animated(object):
    """Animated beings can move"""
    def __init__(self, coords):
        self.coords = coords
        self.old_coords = None
        self.months = 0
    def where(self):
        return c_neighbors(self.coords)
    def choose_new_coords(self):
        self.old_coords = self.coords
        possible = self.where()
        if possible:
            direction = choice(self.where())
            self.coords = [(self.coords[i]+direction[i]) % NGRID for i in range(2)]
#    def __del__(self):
#        print "died at "+ str(self.coords)


class Lumberjack(Animated):
    """Lumberjacks chop down trees"""
    def __init__(self, coords):
        super(Lumberjack, self).__init__(coords)
        self.nb_cut = 0
        self.nb_lumber = 0
        self.name = gen_name("l")
    def __str__(self):
        return "x"

class Bear(Animated):
    """Bears maul"""
    def __init__(self, coords):
        super(Bear, self).__init__(coords)
        self.nb_maul = 0
        self.name = gen_name("b")
    def where(self):
        return c_land_neighbors(self.coords)
    def __str__(self):
        return "o"

###list of coords
def c_neighbors(coords):
    """returns the list of coordinates of adjacent cells"""
    return [[(coords[0] + i) % NGRID, (coords[1] + j) % NGRID] \
            for i in [-1, 0, 1] \
            for j in [-1, 0, 1] \
            if (i,j) != (0, 0)]

def c_empty_neighbors(coords):
    """returns the list of coordinates of adjacent cells that are empty """
    return [[i, j] for [i,j] in c_neighbors(coords) if grid[i][j] == []]

def c_land_neighbors(coords):
    """returns the list of coordinates of adjacent cells that contain not Trees
    for bears"""
    return [[i, j] for [i,j] in c_neighbors(coords)\
            if (grid[i][j] == []) or (not isinstance(grid[i][j][0], Tree))]

def c_empty_cells():
    """returns list of coords of empty cells in the grid"""
    return [[i, j] for i in range(NGRID) for j in range(NGRID) if grid[i][j] == []]

def c_not_bear_cells():
    """returns list of coords of cells without bear"""
    return [[i, j] for i in range(NGRID) for j in range(NGRID) \
            if not isinstance(grid[i][j], Bear)]

###one less
def maul(lumberjack):
    """a lumberjack will die"""
    global n_maul
    n_maul = n_maul + 1
    d_total['maul'] = d_total['maul'] + 1
    remove_from_grid(lumberjack.coords, lumberjack)
    return lumberjack.name + " is sent to hospital" + check_lumberjacks()

def capture_bear():
    """too many mauls, a Zoo traps a bear"""
    d_total['capture'] = d_total['capture'] + 1
    bear = choice(get_bears())
    remove_from_grid(bear.coords, bear)
    return bear.name + " has been captured"

def fire_lumberjack():
    """the job is not done correctly, one lumberjack is let go"""
    d_total['fired'] = d_total['fired'] + 1
    lumberjack = choice(get_lumberjacks())
    remove_from_grid(lumberjack.coords, lumberjack)
    return lumberjack.name + " has been fired" + check_lumberjacks()

def remove_from_grid(coords, item):
    """remove item from the grid at the coords"""
    grid[coords[0]][coords[1]].remove(item)
    del item

###one more
def new_animate(class_):
    """a new lumberjack or bear joins the forest"""
    if class_==Bear:
        d_total['bears'] = d_total['bears'] + 1
        x, y = choice(c_empty_cells())
    else:
        d_total['lumberjacks'] = d_total['lumberjacks'] + 1
        x, y = choice(c_not_bear_cells())
    new_being = class_([x,y])
    grid[x][y].append(new_being)
    return "a new " + class_.__name__ + " enters the forest: " + new_being.name

def check_lumberjacks():
    """we will never reduce our Lumberjack labor force below 0"""
    if len(get_lumberjacks())==0:
        return " - no more lumberjack, " + new_animate(Lumberjack)
    return ""

###movements
def move_on_grid(being):
    [x, y] = being.old_coords
    grid[x][y].remove(being)
    [x, y] = being.coords
    grid[x][y].append(being)

def move_lumberjack(lumberjack):
    """Lumberjacks move 3 times if they don't encounter a (Elder) Tree or a Bear"""
    global n_lumbers
    for _ in range(3):
        lumberjack.choose_new_coords()
        move_on_grid(lumberjack)
        [x, y] = lumberjack.coords
        #is there something at the new coordinate?
        #move append so this lumberjack is at the end
        if grid[x][y][:-1] != []:
            if isinstance(grid[x][y][0], Tree):
                the_tree = grid[x][y][0]
                price = worth(the_tree)
                if price > 0:
                    lumberjack.nb_cut = lumberjack.nb_cut + 1
                    d_most['n_cut'] = max((lumberjack.nb_cut, lumberjack.name), \
                                           d_most['n_cut'])
                    d_total['cut'] = d_total['cut'] + 1
                    n_lumbers = n_lumbers + price
                    d_total['lumbers'] = d_total['lumbers'] + 1
                    lumberjack.nb_lumber = lumberjack.nb_lumber + price
                    d_most['n_lumber'] = max(d_most['n_lumber'], \
                                             (lumberjack.nb_lumber, lumberjack.name))
                    remove_from_grid([x, y], the_tree)
                    return lumberjack.name + " cuts 1 " + the_tree.typeof
            #if there is a bear, all lumberjacks have been sent to hospital
            if isinstance(grid[x][y][0], Bear):
                #the first bear is the killer
                b = grid[x][y][0]
                b.nb_maul = b.nb_maul + 1
                d_most['n_maul'] = max((b.nb_maul, b.name), d_most['n_maul'])
                return maul(lumberjack)
    return None

def move_bear(bear):
    """Bears move 5 times if they don't encounter a Lumberjack"""
    for _ in range(5):
        bear.choose_new_coords()
        move_on_grid(bear)
        [x, y] = bear.coords
        there_was_something = (grid[x][y][:-1] != [])
        if there_was_something:
            #bears wander where there is no tree
            #so it's either a lumberjack or another bear
            #can't be both.
            if isinstance(grid[x][y][0], Lumberjack):
                bear.nb_maul = bear.nb_maul + 1
                d_most['n_maul'] = max((bear.nb_maul, bear.name), \
                                       d_most['n_maul'])
                return maul(grid[x][y][0])
    return None

###get objects
def get_objects(class_):
    """get a list of instances in the grid"""
    l = []
    for i in range(NGRID):
        for j in range(NGRID):
          if grid[i][j]:
              for k in grid[i][j]:
                  if isinstance(k, class_):
                      l.append(k)
    return l

def get_trees():
    """list of trees"""
    return get_objects(Tree)

def get_bears():
    """list of bears"""
    return get_objects(Bear)

def get_lumberjacks():
    """list of lumberjacks"""
    return get_objects(Lumberjack)

###utils
def gen_name(which="l"):
    """generate random name"""
    name = ""
    for _ in range(randint(1,4)):
        name = name + choice("bcdfghjklmnprstvwxz") + choice("auiey")
    if which == "b":
        name = name[::-1]
    return name.capitalize()

def worth(tree):
    """pieces for a tree"""
    if tree.typeof == 'Elder Tree':
        return 2
    if tree.typeof == 'Tree':
        return 1
    return 0

def one_month():
    """a step of one month"""
    events = []
    global tick
    tick = tick + 1
    #each Tree can spawn a new sapling
    for t in get_trees():
        l_empty_spaces = c_empty_neighbors(t.coords)
        percent = 10 if t.typeof == 'Tree' else \
                  20 if t.typeof == 'Elder Tree' else 0
        if (randint(1,100) < percent):
            if l_empty_spaces:
                [x, y] = choice(l_empty_spaces)
                grid[x][y] = [Tree([x,y])]
                d_total['trees'] = d_total['trees'] + 1
        t.grow()
        d_oldest['tree'] = max(t.months, d_oldest['tree'])
    #each lumberjack/bear moves
    for l in get_lumberjacks():
        l.months = l.months + 1
        d_oldest['lumberjack'] = max((l.months, l.name), \
                                     d_oldest['lumberjack'])
        event = move_lumberjack(l)
        if event:
            events.append(event)
    for b in get_bears():
        b.months = b.months + 1
        d_oldest['bear'] = max((b.months, b.name), d_oldest['bear'])
        event = move_bear(b)
        if event:
            events.append(event)
    return events

def print_grid():
    """print the grid
    if more than 1 thing is at a place, print the last.
    At 1 place, there is
    - at most a tree and possibly several lumberjack
    - or 1 bear
    """
    print "-" * 2 * NGRID
    print '\n'.join([' '.join([str(i[-1]) if i != [] else ' ' \
                               for i in line]) \
                     for line in grid])
    print "-" * 2 * NGRID

def clean():
    """clear the console"""
    os.system('cls' if os.name == 'nt' else 'clear')

def print_grid_and_events():
    """print grid and list of events"""
    clean()
    print_grid()
    if VERBOSE:
        print '\n'.join(events)
        print "-" * 2 * NGRID

###populate the forest
l = c_empty_cells()
shuffle(l)
for x, y in l[:((NGRID*NGRID) / 2)]:
    grid[x][y] = [Tree([x, y], 12, 'Tree')]
    d_total['trees'] = d_total['trees'] + 1

l = c_empty_cells()
shuffle(l)
for x, y in l[:((NGRID*NGRID) / 10)]:
    grid[x][y] = [Lumberjack([x, y])]
    d_total['lumberjacks'] = d_total['lumberjacks'] + 1

l = c_empty_cells()
shuffle(l)
for x, y in l[:((NGRID*NGRID) / 10)]:
    grid[x][y] = [Bear([x, y])]
    d_total['bears'] = d_total['bears'] + 1

###time goes on
while (tick <= DURATION and len(get_trees())>0):
    events = one_month()
    #end of the year
    if (tick % 12)==0:
        events.append("End of the year")
        #lumber tracking
        nlumberjacks = len(get_lumberjacks())
        events.append(str(n_lumbers) + " lumbers VS " +\
                      str(nlumberjacks) + " Lumberjacks")
        if n_lumbers >= nlumberjacks:
            n_hire = n_lumbers/nlumberjacks
            events.append("we hire " + str(n_hire) +\
                          " new Lumberjack" + ("s" if (n_hire > 1) else ""))
            for _ in range(n_hire):
                events.append(new_animate(Lumberjack))
        else:
            events.append(fire_lumberjack())
        d_year['n_lumber'] = max(d_year['n_lumber'], n_lumbers)
        n_lumbers = 0
        #maul tracking
        events.append("maul this year: " + str(n_maul))
        if n_maul == 0:
            events.append(new_animate(Bear))
        else:
            events.append(capture_bear())
        d_year['n_maul'] = max(d_year['n_maul'], n_maul)
        n_maul = 0
    print_grid_and_events()
    sleep(SLEEPTIME)

print "-"*70
print "End of the game"
print "-"*70
print "month:" + str(tick - 1)
print "number of trees still alive: " + str(len(get_trees()))
print "number of lumberjacks still alive: " + str(len(get_lumberjacks()))
print "number of bears still alive: " + str(len(get_bears()))

print "-"*70
print "oldest Tree ever is/was: " + str(d_oldest['tree'])
print "oldest Lumberjack ever is/was: " + str(d_oldest['lumberjack'][0]) + \
    " yo " + d_oldest['lumberjack'][1]
print "oldest Bear ever is/was: " + str(d_oldest['bear'][0]) + \
    " yo " + d_oldest['bear'][1]
print "-"*70
print "max cut by a Lumberjack: " + str(d_most['n_cut'][0]) + \
    " by " + str(d_most['n_cut'][1])
print "max lumber by a Lumberjack: " + str(d_most['n_lumber'][0]) + \
    " by " + str(d_most['n_lumber'][1])
print "max maul by a Bear: " + str(d_most['n_maul'][0]) + \
    " by " + str(d_most['n_maul'][1])
print "-"*70
print "max lumber in a year: " + str(d_year['n_lumber'])
print "max maul in a year: " + str(d_year['n_maul'])
print "-"*70
print "Total of:"
for i, j in d_total.items():
    print i, str(j)

Деякі результати:

------------------------------
          x



    I
    I
  x   i                     I

      i i i   i       I I x i
i   I   I i I I   i i     o
      i i I I I           i
    i I   x i
    I I   I I
      I     I i
            x
------------------------------
Dy is sent to hospital
Lehuniru cuts 1 Tree
------------------------------

Кінець року

------------------------------
            x

    x

i I     I
    i     I               x
      I
                          i i
      x                   I I
  i I   i I     i       i
  I         i I
            i x i
    I         i I
          o
        x
------------------------------
Fuha cuts 1 Tree
Ka cuts 1 Tree
Ky is sent to hospital
End of the year
11 lumbers VS 4 Lumberjacks
we hire 2 new Lumberjacks
a new Lumberjack enters the forest: Di
a new Lumberjack enters the forest: Dy
maul this year: 6
Evykut has been captured
------------------------------

Кінець гри

------------------------------
          x
  i
        x     x

          x                 x
                  x i     x
    i               I
    I i x x   I i           x
                    x   i   i
      x i i i i I
      i i I   I i I   i
I       i     i i
        i   x   i
            I   i I
    I I   x i   I I         x
------------------------------
Vanabixy cuts 1 Tree
Fasiguvy cuts 1 Tree
------------------------------
----------------------------------------------------------------------
End of the game
----------------------------------------------------------------------
month:4800
number of trees still alive: 36
number of lumberjacks still alive: 15
number of bears still alive: 0
----------------------------------------------------------------------
oldest Tree ever is/was: 129
oldest Lumberjack ever is/was: 308 yo Cejuka
oldest Bear ever is/was: 288 yo Ekyx
----------------------------------------------------------------------
max cut by a Lumberjack: 44 by Cejuka
max lumber by a Lumberjack: 44 by Cejuka
max maul by a Bear: 52 by Ekyx
----------------------------------------------------------------------
max lumber in a year: 84
max maul in a year: 86
----------------------------------------------------------------------
Total of:
bears 211
cut 5054
fired 67
capture 211
lumberjacks 1177
lumbers 5054
maul 1095
trees 5090
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.