diff --git a/.idea/.idea.Life/.idea/contentModel.xml b/.idea/.idea.Life/.idea/contentModel.xml index bbb2548..c8da571 100644 --- a/.idea/.idea.Life/.idea/contentModel.xml +++ b/.idea/.idea.Life/.idea/contentModel.xml @@ -23,6 +23,7 @@ + @@ -36,6 +37,7 @@ + diff --git a/Life/Automaton/BasicField.cs b/Life/Automaton/BasicField.cs index 745fa2e..100db3f 100644 --- a/Life/Automaton/BasicField.cs +++ b/Life/Automaton/BasicField.cs @@ -46,6 +46,11 @@ namespace Life.Automaton _observers.ForEach(observer => observer.OnNext(this)); } + public void Notify() + { + _observers.ForEach(observer => observer.OnNext(this)); + } + public event PropertyChangedEventHandler PropertyChanged; [NotifyPropertyChangedInvocator] diff --git a/Life/Automaton/GameOfLife.cs b/Life/Automaton/GameOfLife.cs index 76e2886..02a1f22 100644 --- a/Life/Automaton/GameOfLife.cs +++ b/Life/Automaton/GameOfLife.cs @@ -2,9 +2,14 @@ namespace Life.Automaton { - public static class GameOfLife + public class GameOfLife : IAutomaton { - public static Rule Rule(string born, string survive) + public GameOfLife(int width, int height) + { + Initialize(new MooreField(width, height)); + } + + public static Rule MakeRule(string born, string survive) { bool WillSurvive(int neighbours) => survive.Contains(neighbours.ToString()); bool WillBorn(int neighbours) => born.Contains(neighbours.ToString()); @@ -29,5 +34,22 @@ namespace Life.Automaton return @new; }; } + + public Rule Rule { get; set; } = MakeRule("3", "32"); + + public int Iteration { get; private set; } + + public IField Field { get; private set; } + + public void Initialize(IField field) + { + Field = field; + } + + public void Advance() + { + Field.Transform(Rule); + Iteration++; + } } } \ No newline at end of file diff --git a/Life/Automaton/IField.cs b/Life/Automaton/IField.cs index 707ddce..de3905d 100644 --- a/Life/Automaton/IField.cs +++ b/Life/Automaton/IField.cs @@ -17,5 +17,6 @@ namespace Life.Automaton IEnumerable Neighbours(int x, int y); void Transform(Rule rule); + void Notify(); } } \ No newline at end of file diff --git a/Life/AutomatonField.xaml b/Life/AutomatonField.xaml index a51480c..baf9954 100644 --- a/Life/AutomatonField.xaml +++ b/Life/AutomatonField.xaml @@ -47,7 +47,7 @@ - + diff --git a/Life/MainWindow.xaml.cs b/Life/MainWindow.xaml.cs index c54c3ab..c731db2 100644 --- a/Life/MainWindow.xaml.cs +++ b/Life/MainWindow.xaml.cs @@ -1,63 +1,28 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Runtime.CompilerServices; -using System.Windows; -using Life.Annotations; +using System.Windows; using Life.Automaton; +using Life.ViewModel; namespace Life { /// /// Interaction logic for MainWindow.xaml /// - public partial class MainWindow : Window, INotifyPropertyChanged + public partial class MainWindow : Window { - private IField _field; - private int _size; - private Rule _rule = GameOfLife.Rule("3", "23"); - - public IField Field - { - get => _field; - set - { - _field = value; - OnPropertyChanged(nameof(Field)); - } - } - - public int Size - { - get => _size; - set - { - _size = value; - OnPropertyChanged(nameof(Size)); - } - } + public AutomatonViewModel ViewModel; public MainWindow() { - Size = 20; - Field = new MooreField(20, 20); - Field[5, 5].IsAlive = true; - DataContext = this; + ViewModel = new AutomatonViewModel() + { + Automaton = new GameOfLife(20, 20), + Size = 16, + Separation = 2 + }; + + DataContext = ViewModel; InitializeComponent(); } - - private void Step_OnClick(object sender, RoutedEventArgs e) - { - Field.Transform(_rule); - } - - public event PropertyChangedEventHandler PropertyChanged; - - [NotifyPropertyChangedInvocator] - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } } } \ No newline at end of file diff --git a/Life/Misc/DelegateCommand.cs b/Life/Misc/DelegateCommand.cs new file mode 100644 index 0000000..361d142 --- /dev/null +++ b/Life/Misc/DelegateCommand.cs @@ -0,0 +1,26 @@ +using System; +using System.Windows.Input; + +namespace Life.Misc +{ + public class DelegateCommand : ICommand + { + public Action Action { get; set; } + public Predicate Predicate { get; set; } + + public bool CanExecute(object parameter) + { + if (Action == null) + return false; + + return Predicate?.Invoke((T) parameter) ?? true; + } + + public void Execute(object parameter) + { + Action?.Invoke((T)parameter); + } + + public event EventHandler CanExecuteChanged; + } +} \ No newline at end of file diff --git a/Life/ViewModel/AutomatonViewModel.cs b/Life/ViewModel/AutomatonViewModel.cs new file mode 100644 index 0000000..7ac733e --- /dev/null +++ b/Life/ViewModel/AutomatonViewModel.cs @@ -0,0 +1,70 @@ +using System; +using System.Windows.Input; +using Life.Automaton; +using Life.Misc; + +namespace Life.ViewModel +{ + public class AutomatonViewModel : BaseViewModel + { + + private int _size; + private int _separation; + private IAutomaton _automaton; + + public int Size + { + get => _size; + set + { + _size = value; + OnPropertyChanged(nameof(Size)); + } + } + + public int Separation + { + get => _separation; + set + { + _separation = value; + OnPropertyChanged(nameof(Separation)); + } + } + + public IAutomaton Automaton + { + get => _automaton; + set + { + _automaton = value; + OnPropertyChanged(nameof(Field)); + OnPropertyChanged(nameof(Iteration)); + } + } + + public IField Field => Automaton.Field; + public int Iteration => Automaton.Iteration; + + public ICommand CellClicked => new DelegateCommand + { + Action = HandleCellClick + }; + + public ICommand StepCommand => new DelegateCommand + { + Action = Step + }; + + private void Step(object param) + { + Automaton.Advance(); + } + + private void HandleCellClick(Position position) + { + Field[position.X, position.Y].IsAlive = !Field[position.X, position.Y].IsAlive; + Field.Notify(); + } + } +} \ No newline at end of file