<file_sep>﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GameOfLife
{
    public partial class LifeGrid : UserControl
    {
        private bool[][] alive;
        public Color colorAlive;
        public Color colorDead;
        public List<int> rulesCreate;
        public List<int> rulesSurvive;
        private Point mouseLocation;
        private int cellSize;
        public bool brush;
        public int liveCells;

        public int getCellSize()
        {
            return cellSize;
        }

        public LifeGrid()
        {
            liveCells = 0;
            cellSize = 4;
            brush = true;
            InitializeComponent();
            Random rnd = new Random();
            alive = new bool[Height/cellSize][];
            for (int i = 0; i < Height/cellSize; i++)
            {
                alive[i] = new bool[Width/cellSize];
                for (int j = 0; j < Width/cellSize; j++)
                {
                    alive[i][j] = false; //rnd.Next() % 2 == 0;
                }
            }

            rulesCreate = new List<int>();
            rulesCreate.Add(3);

            rulesSurvive = new List<int>();
            rulesSurvive.Add(2);
            rulesSurvive.Add(3);

            colorAlive = Color.Green;
            colorDead = Color.Black;
           
            pictureBox1.Image = new Bitmap(Width, Height);
            mouseLocation = Point.Empty;
            drawCurrentIteration();

        }

        public void drawCurrentIteration()
        {
            SolidBrush brushA = new SolidBrush(colorAlive);
            SolidBrush brushD = new SolidBrush(colorDead);
            liveCells = 0;
            for (int i=0; i<Height/cellSize; i++)
            {
                for(int j=0; j<Width/cellSize; j++)
                {
                    using(var canvas = Graphics.FromImage(pictureBox1.Image))
                    {
                        if (alive[i][j])
                        {
                            canvas.FillRectangle(brushA, i * cellSize, j * cellSize, cellSize, cellSize);
                            liveCells++;
                        }
                        else
                        {
                            canvas.FillRectangle(brushD, i * cellSize, j * cellSize, cellSize, cellSize);
                        }

                    }
                }
            }
            pictureBox1.Refresh();
            brushA.Dispose();
            brushD.Dispose();
        }

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if(e.Button == MouseButtons.Left)
            {
                if (e.Location.X <= Height && e.Location.X >= 0 && e.Location.Y <= Width && e.Location.Y >= 0)
                {
                    mouseLocation = e.Location;
                    alive[mouseLocation.X / cellSize][mouseLocation.Y / cellSize] = brush;
                    drawCurrentIteration();
                }
            }
        }

        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if(e.Button == MouseButtons.Left && mouseLocation != Point.Empty)
            {
                if (e.Location.X <= Height && e.Location.X >= 0 && e.Location.Y <= Width && e.Location.Y >= 0)
                {
                    int x0 = mouseLocation.X/cellSize, y0 = mouseLocation.Y/cellSize, x1 = e.X/cellSize, y1 = e.Y/cellSize;

                    bool steep = Math.Abs(y1 - y0) > Math.Abs(x1 - x0);
                    if (steep) {
                        int t = x0;
                        x0 = y0;
                        y0 = t;
                        t = x1;
                        x1 = y1;
                        y1 = t;
                    }
                    if (x0 > x1) {
                        int t = x0;
                        x0 = x1;
                        x1 = t;
                        t = y0;
                        y0 = y1;
                        y1 = t;
                    }
                    int dX = (x1 - x0), dY = Math.Abs(y1 - y0), err = (dX / 2), ystep = (y0 < y1 ? 1 : -1), y = y0;

                    for (int x = x0; x <= x1; ++x)
                    {
                        if (x < 0 || x >= Width / cellSize || y < 0 || y >= Height / cellSize)
                            break;
                        if (steep)
                            alive[y][x] = brush;
                        else
                            alive[x][y] = brush;
                            err = err - dY;
                        if (err < 0) { y += ystep; err += dX; }
                    }

                    mouseLocation = e.Location;
                    //alive[mouseLocation.X / cellSize][mouseLocation.Y / cellSize] = brush;
                    drawCurrentIteration();
                }
            }
        }

        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            mouseLocation = Point.Empty;
        }

        public void computeNextState()
        {
            bool[][] tmp = new bool[Height/cellSize][];
            for(int i=0; i<Height/cellSize; i++)
            {
                tmp[i] = new bool[Width/cellSize];
                for(int j=0; j<Width/cellSize; j++)
                {
                    int count = 0;
                    if(i != 0)
                    {
                        if (j != 0 && alive[i - 1][j - 1])
                            count++;
                        if (alive[i-1][j])
                            count++;
                        if (j < Width/cellSize - 1 && alive[i - 1][j + 1])
                            count++;
                    }
                    if (i < Height/cellSize - 1)
                    {
                        if (j != 0 && alive[i + 1][j - 1])
                            count++;
                        if (alive[i+1][j])
                            count++;
                        if (j < Width/cellSize - 1 && alive[i + 1][j + 1])
                            count++;
                    }
                    if (j != 0 && alive[i][j - 1])
                        count++;
                    if (j < Width/cellSize - 1 && alive[i][j + 1])
                        count++;

                    if (alive[i][j])
                    {
                        bool flag = true;
                        foreach(int rule in rulesSurvive)
                        {
                            if (rule == count)
                            {
                                flag = false;
                                tmp[i][j] = true;
                                break;
                            }
                        }
                        if (flag)
                            tmp[i][j] = false;
                    }
                    else
                    {
                        bool flag = true;
                        foreach (int rule in rulesCreate)
                        {
                            if (rule == count)
                            {
                                flag = false;
                                tmp[i][j] = true;
                                break;
                            }
                        }
                        if (flag)
                            tmp[i][j] = false;
                    }
                }
            }
            alive = tmp;
            drawCurrentIteration();
        }

        private void LifeGrid_Resize(object sender, EventArgs e)
        {
            alive = new bool[Height / cellSize][];
            for (int i = 0; i < Height / cellSize; i++)
            {
                alive[i] = new bool[Width / cellSize];
                for (int j = 0; j < Width / cellSize; j++)
                {
                    alive[i][j] = false; //rnd.Next() % 2 == 0;
                }
            }
            pictureBox1.Image.Dispose();
            pictureBox1.Image = new Bitmap(Width, Height);
            pictureBox1.Refresh();
            drawCurrentIteration();
        }

        public void changeSize(int size)
        {
            cellSize = size;
            LifeGrid_Resize(null, null);
        }
        public void fillRandom()
        {
            Random rnd = new Random();
            for (int i = 0; i < Height / cellSize; i++)
            {
                for (int j = 0; j < Width / cellSize; j++)
                {
                    alive[i][j] = rnd.Next() % 2 == 0;
                }
            }
            drawCurrentIteration();
        }
        public void clear()
        {
            for (int i = 0; i < Height / cellSize; i++)
                for (int j = 0; j < Width / cellSize; j++)
                    alive[i][j] = false;
            drawCurrentIteration();
        }
        public Image getImg()
        {
            return pictureBox1.Image;
        }
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GameOfLife
{
    public partial class Form1 : Form
    {
        int childN;
        public Form1()
        {
            childN = 1;
            InitializeComponent();
            Text = "Game of Life";
        }
          
        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            LifeForm child = new LifeForm();
            child.Text = "Game " + childN.ToString();
            child.MdiParent = this;
            child.Show();
            childN++;
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        
        private void toolStripMenuItem1_Click(object sender, EventArgs e)
        {
            if(ActiveMdiChild != null)
                ((LifeForm)ActiveMdiChild).saveToolStripMenuItem_Click(null, null);
        }
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GameOfLife
{
    public partial class LifeForm : Form
    {
        public LifeForm()
        {
            InitializeComponent();
            colorDialog.Color = lifeGrid1.colorAlive;
            aliveToolStripMenuItem.ForeColor = lifeGrid1.colorAlive;
            deadToolStripMenuItem.ForeColor = lifeGrid1.colorDead;
            clickTypeToolStripMenuItem.ForeColor = lifeGrid1.colorAlive;
        }

        

        private void timer1_Tick(object sender, EventArgs e)
        {
            lifeGrid1.computeNextState();
        }

        private void LifeForm_ResizeEnd(object sender, EventArgs e)
        {
            if (Height < Width)
                Height = Width + 48;
            else
                Width = Height - 48;
        }
        
        private void timer2_Tick(object sender, EventArgs e)
        {
            statusCellNumber.Text = lifeGrid1.liveCells.ToString();
        }

        private void playToolStripMenuItem_Click(object sender, EventArgs e)
        {
            timer1.Enabled = !timer1.Enabled;
            if (timer1.Enabled)
                playToolStripMenuItem.Text = "Pause";
            else
                playToolStripMenuItem.Text = "Play";
        }

        private void aliveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (colorDialog.ShowDialog() == DialogResult.OK)
            {
                lifeGrid1.colorAlive = colorDialog.Color;
                aliveToolStripMenuItem.ForeColor = colorDialog.Color;
                lifeGrid1.drawCurrentIteration();
                if (lifeGrid1.brush)
                    clickTypeToolStripMenuItem.ForeColor = colorDialog.Color;
            }
        }

        private void deadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (colorDialog.ShowDialog() == DialogResult.OK)
            {
                lifeGrid1.colorDead = colorDialog.Color;
                deadToolStripMenuItem.ForeColor = colorDialog.Color;
                lifeGrid1.drawCurrentIteration();
                if (!lifeGrid1.brush)
                    clickTypeToolStripMenuItem.ForeColor = colorDialog.Color;
            }
        }

        private void rulesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            timer1.Enabled = false;
            FormRules frm = new FormRules(lifeGrid1, timer1);
            frm.Show();
            playToolStripMenuItem.Text = "Play";
        }

        private void clickTypeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            lifeGrid1.brush = !lifeGrid1.brush;
            if (lifeGrid1.brush)
            {
                clickTypeToolStripMenuItem.Text = "Mode: Create";
                clickTypeToolStripMenuItem.ForeColor = aliveToolStripMenuItem.ForeColor;
            }
            else
            {
                clickTypeToolStripMenuItem.Text = "Mode: Destroy";
                clickTypeToolStripMenuItem.ForeColor = deadToolStripMenuItem.ForeColor;
            }
        }

        private void randomToolStripMenuItem_Click(object sender, EventArgs e)
        {
            lifeGrid1.fillRandom();
        }

        private void clearToolStripMenuItem_Click(object sender, EventArgs e)
        {
            lifeGrid1.clear();
        }

        public void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog saveFileDialog1 = new SaveFileDialog();
            saveFileDialog1.Filter =
            "JPeg Image|*.jpg|Bitmap Image|*.bmp|Gif Image|*.gif";
            saveFileDialog1.Title = "Save an Image File";


            if (saveFileDialog1.ShowDialog() != DialogResult.Cancel)
            {
                try
                {
                    Image tmp = lifeGrid1.getImg();
                    tmp.Save(saveFileDialog1.FileName);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not save file \"" +
                    saveFileDialog1.FileName + "\" to disk. Original error: " +
                    ex.Message);
                }
            }
        }
    }
}
<file_sep># Game of Life
# <NAME> 151524
# Опис на апликација:
Апликацијата што ја развив е имлементација на Conway's Game of Life проширена со опции да се менуваат правилата на креирање и останување во живот како и соодветен кориснички интерфејс.
# Упатство на користење:
# Нова игра:
Корисникот може да отвори нова игра преку File -> New. Можат да работат повеќе игри во исто време.
Интеракција со играта:
Корисникот може во секое време да “црта” живи клетки во панелот или да ги брише со кликање и влечење на маусот. Менување помеѓу овие два начина на работа се прави преку кликање на “Mode: Create”/”Mode: Destroy” во менито.
# Play/Pause:
Преку “Play/Pause” копчето во менито се запира или продолжува со евулузијата на играта.
# Промена на бои:
Боите на живите и мртвите клетки можат да се сменат преку Colors во менито за опции.
# Дополнителни опции:
Во менито “Tools” корисникот може да добие нова случајно генерирана мапа или празна.
Промена на правла за живот:
При кликање на “Rules” во менито се отвара ново прозорче со правилата на играта. Во тоа прозорче корисникот може да додаде или отстрани правила, како и да ја смени големината на клетките и времето помеѓу еволуции. Секоја игра почнува со основните правила на играта. Ако има три или две живи клетки околу жива клетка, таа останува жива во следната итерација. Ако има три живи клетки околу мртва клетка, таа оживува. Во сите други случаеви клетката умира или останува умрена.
# Зачувување:
Воведена е и опцијата корисникот да може да зачува слика од играта преку “File -> Save”.
# Претставување на проблемот:
За чување на состојбата на клетките се користи матрица со bool елементи каде true означува дека е жива и false дека е мртва. Матрицата во себе држи број на елементи пресметано со височината на контејнерот помножена со должината поделено со должината на страните на една клетка. Бројот на елементи е динамичен и се мења со променување на големината на прозорецот.
Правилата за живот се држат во две листи, и при секое итерирање се проверуваат дали важат за одредена клетка.
Текот на животот се води со помош на тајмер кој ја повикува функцијата за еволуција и прикажување на следната состојба.
За читање и цртање со зимање на инпут од маусот на корисникот се користи “Bresenham's Line Algorithm” за цртање на линија врз пиксели. 
# Кориснички интерфејс:
Состојбата на играта е прикажана со цртање на квадрати со бои кој што можат да се променат од корисникот врз PictureBox. Сите алатки и опции се достапни преку менито. Бројот на живи клетки се прикажани на статус лентата на дното од прозорецот. Корисникот може да отвори повеќе игри истовремено. Оваа функционалност е овозможена преку MDI контејнер, со цел да може да спореди еволуција според различни правила. 
<file_sep>﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GameOfLife
{
    public partial class FormRules : Form
    {
        LifeGrid Grid;
        Timer Tmr;
        public FormRules(LifeGrid grid, Timer tmr)
        {
            InitializeComponent();
            Grid = grid;
            Tmr = tmr;
            foreach (int rule in grid.rulesCreate)
                listBoxCreation.Items.Add(rule);
            foreach (int rule in grid.rulesSurvive)
                listBoxSurvival.Items.Add(rule);
            numericUpDownSize.Value = grid.getCellSize();
            numericUpDownTimer.Value = tmr.Interval;
        }

        private void numericUpDownSize_ValueChanged(object sender, EventArgs e)
        {
            Grid.changeSize((int) numericUpDownSize.Value);
        }

        private void removeSurvivalRule_Click(object sender, EventArgs e)
        {
            if(listBoxSurvival.Items.Count != 0 && listBoxSurvival.SelectedIndex != -1)
            {
                Grid.rulesSurvive.Remove((int)listBoxSurvival.Items[listBoxSurvival.SelectedIndex]);
                listBoxSurvival.Items.RemoveAt(listBoxSurvival.SelectedIndex);
            }
        }

        private void addSurvivalRule_Click(object sender, EventArgs e)
        {
            if (!listBoxSurvival.Items.Contains(numericUpDownSurvival.Value))
            {
                Grid.rulesSurvive.Add((int)numericUpDownSurvival.Value);
                listBoxSurvival.Items.Add((int)numericUpDownSurvival.Value);
            }
        }

        private void removeCreationRule_Click(object sender, EventArgs e)
        {
            if (listBoxCreation.Items.Count != 0 && listBoxCreation.SelectedIndex != -1)
            {
                Grid.rulesCreate.Remove((int)listBoxCreation.Items[listBoxCreation.SelectedIndex]);
                listBoxCreation.Items.RemoveAt(listBoxCreation.SelectedIndex);
            }
        }

        private void addCreationRule_Click(object sender, EventArgs e)
        {
            if (!listBoxCreation.Items.Contains(numericUpDownCreation.Value))
            {
                Grid.rulesCreate.Add((int)numericUpDownCreation.Value);
                listBoxCreation.Items.Add((int)numericUpDownCreation.Value);
            }
        }

        private void buttonSize_Click(object sender, EventArgs e)
        {
            Grid.changeSize((int)numericUpDownSize.Value);
        }

        private void numericUpDownTimer_ValueChanged(object sender, EventArgs e)
        {
            Tmr.Interval = (int)numericUpDownTimer.Value;
        }
    }
}
