diff --git a/TestTetris/GridTest.cs b/TestTetris/GridTest.cs index f494d0d..48f0926 100644 --- a/TestTetris/GridTest.cs +++ b/TestTetris/GridTest.cs @@ -133,14 +133,16 @@ public class GridTest { [Test] public void LineFull() { Console.Out.WriteLine(g.ToString()); - Assert.False(g.LineFull()); + Assert.AreEqual(-1, g.LineFull()); + + int line = random.Next(g.MinGrid.Y, g.MaxGrid.Y); for (int x = 0; x < ig.GetLength(0); x++) - ig[x, ig.GetLength(1) - 1] = Color.Aqua; + ig[x, line] = Color.Aqua; Console.Out.WriteLine("=========="); Console.Out.WriteLine(g.ToString()); - Assert.True(g.LineFull()); + Assert.AreEqual(line, g.LineFull()); } [Test] @@ -150,16 +152,17 @@ public class GridTest { ig[x, y] = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256)); } + int line = random.Next(g.MinGrid.Y, g.MaxGrid.Y); for (int x = 0; x < ig.GetLength(0); x++) - ig[x, ig.GetLength(1) - 1] = Color.Aqua; + ig[x, line] = Color.Aqua; Color[,] olg_grid = (Color[,])ig.Clone(); Console.Out.WriteLine(g.ToString()); - g.ClearLine(); + g.ClearLine(line); Console.Out.WriteLine("=========="); Console.Out.WriteLine(g.ToString()); for (uint x = 0; x < ig.GetLength(0); x++) - for (uint y = 1; y < ig.GetLength(1); y++) { + for (uint y = 1; y <= line; y++) { Assert.AreEqual(olg_grid[x, y-1], ig[x,y]); } diff --git a/Tetris/Models/Game.cs b/Tetris/Models/Game.cs index 2a1483f..4411e88 100644 --- a/Tetris/Models/Game.cs +++ b/Tetris/Models/Game.cs @@ -61,12 +61,16 @@ public class Game : INotifyPropertyChanged { return _grid.MinGrid.Y == _currentTetrominoe.Coordinates.Y; } - public bool LineFull() { - return _grid.LineFull(); - } - - public void ClearLine() { - _grid.ClearLine(); + public void ClearLine() + { + int line = _grid.LineFull(); + + while (line != -1) + { + _grid.ClearLine(line); + Score += Grid.CGrid.GetLength(0); + line = _grid.LineFull(); + } } public void PrintTetrominoe() { diff --git a/Tetris/Models/Grid.cs b/Tetris/Models/Grid.cs index 6547716..7d57eab 100644 --- a/Tetris/Models/Grid.cs +++ b/Tetris/Models/Grid.cs @@ -38,18 +38,32 @@ public class Grid { return true; } - public bool LineFull() { - return Enumerable.Range(0, _grid.GetLength(0)).Select(x => _grid[x, MaxGrid.Y]).All(x => x != Color.Empty); + public int LineFull() + { + for (int y = MaxGrid.Y; y > 0; y--) + { + bool full = true; + for (int x = 0; x <= MaxGrid.X; x++) + if (_grid[x, y] == Color.Empty) + { + full = false; + break; + } + if (full) + return y; + } + + return -1; } - public void ClearLine() { - for (int x = 0; x <= MaxGrid.X; x++) - for (int y = MaxGrid.Y; y > 0; y--) { + public void ClearLine(int line) { + for (int x = MinGrid.X; x <= MaxGrid.X; x++) + for (int y = line; y > MinGrid.Y; y--) { _grid[x, y] = _grid[x, y - 1]; } for (int x = 0; x <= MaxGrid.X; x++) - _grid[x,0] = Color.Empty; + _grid[x,MinGrid.Y] = Color.Empty; } public void PrintTetrominoe(Tetrominoe t) { diff --git a/Tetris/Views/GameWindow.xaml b/Tetris/Views/GameWindow.xaml index 135dfac..80015c7 100644 --- a/Tetris/Views/GameWindow.xaml +++ b/Tetris/Views/GameWindow.xaml @@ -6,6 +6,7 @@ mc:Ignorable="d" Title="GameWindow" Width="{Binding Width}" Height="{Binding Height}" KeyDown="UIElement_OnKeyDown"> + \ No newline at end of file diff --git a/Tetris/Views/GameWindow.xaml.cs b/Tetris/Views/GameWindow.xaml.cs index b6bb3c2..561455f 100644 --- a/Tetris/Views/GameWindow.xaml.cs +++ b/Tetris/Views/GameWindow.xaml.cs @@ -1,20 +1,16 @@ -using System; -using System.Runtime.InteropServices; -using System.Windows; +using System.Runtime.InteropServices; using System.Windows.Input; using Tetris.ViewsModels; namespace Tetris.Views; -public partial class GameWindow : Window +public partial class GameWindow { - private static readonly GameViewModel GameViewModel = new(); - public GameWindow() { AttachConsole(-1); InitializeComponent(); - DataContext = GameViewModel; + DataContext = new GameViewModel(); } [DllImport("kernel32.dll")] @@ -22,28 +18,24 @@ public partial class GameWindow : Window private void UIElement_OnKeyDown(object sender, KeyEventArgs e) { - // If key is space - if (e.Key == Key.Space) + switch (e.Key) { - GameViewModel.Game.CurrentTetrominoe?.RotateRight(); - } - - // If key is down - else if (e.Key == Key.Down) - { - GameViewModel.Game.CurrentTetrominoe?.GoDown(); - } - - // If key is left - else if (e.Key == Key.Left) - { - GameViewModel.Game.CurrentTetrominoe?.GoLeft(); - } - - // If key is right - else if (e.Key == Key.Right) - { - GameViewModel.Game.CurrentTetrominoe?.GoRight(); + // If key is space + case Key.Space: + GameViewModel.Game.CurrentTetrominoe?.RotateRight(); + break; + // If key is down + case Key.Down: + GameViewModel.Game.CurrentTetrominoe?.GoDown(); + break; + // If key is left + case Key.Left: + GameViewModel.Game.CurrentTetrominoe?.GoLeft(); + break; + // If key is right + case Key.Right: + GameViewModel.Game.CurrentTetrominoe?.GoRight(); + break; } } } \ No newline at end of file diff --git a/Tetris/Views/MainWindow.xaml.cs b/Tetris/Views/MainWindow.xaml.cs index 90e910c..ea38c31 100644 --- a/Tetris/Views/MainWindow.xaml.cs +++ b/Tetris/Views/MainWindow.xaml.cs @@ -8,7 +8,7 @@ namespace Tetris.Views; /// /// Interaction logic for MainWindow.xaml /// -public partial class MainWindow : Window +public partial class MainWindow { public MainWindow() { diff --git a/Tetris/ViewsModels/GameViewModel.cs b/Tetris/ViewsModels/GameViewModel.cs index 4d5aac4..0d64eaf 100644 --- a/Tetris/ViewsModels/GameViewModel.cs +++ b/Tetris/ViewsModels/GameViewModel.cs @@ -2,11 +2,13 @@ using System; using System.ComponentModel; +using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Threading; using Tetris.Models; using Color = System.Drawing.Color; +using Grid = Tetris.Models.Grid; namespace Tetris.ViewsModels; @@ -14,7 +16,12 @@ public class GameViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; - public static readonly Game Game = new("...", new Grid(new Color[20, 40])); + protected virtual void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + public static readonly Game Game = new("...", new Grid(new Color[15, 30])); private const int RendererHertz = 5; private const int GameRendererHertz = (1 / RendererHertz) * 1000; @@ -22,6 +29,10 @@ public class GameViewModel : INotifyPropertyChanged private readonly int _width = (Game.Grid.MaxGrid.X + 1) * Multiplier; private readonly int _height = (Game.Grid.MaxGrid.Y + 1) * Multiplier; private readonly WriteableBitmap _writeableBitmap; + private readonly int _colorLine = 0xFFFFFF; + private bool _isPaused; + private uint _countDown; + private string _scoreText = ""; public GameViewModel() { @@ -37,7 +48,7 @@ public class GameViewModel : INotifyPropertyChanged var dispatcherUpdateTimer = new DispatcherTimer { - Interval = new TimeSpan(0, 0, 0, 0, 100) + Interval = new TimeSpan(0, 0, 0, 0, 200) }; dispatcherUpdateTimer.Tick += Update; @@ -46,21 +57,37 @@ public class GameViewModel : INotifyPropertyChanged public ImageSource Source => _writeableBitmap; + public string ScoreText + { + get => _scoreText; + set + { + _scoreText = value; + OnPropertyChanged("ScoreText"); + } + } + private void Render(object? sender, EventArgs eventArgs) { _writeableBitmap.Lock(); _writeableBitmap.Clear(Colors.Black); + ScoreText = "Score: " + Game.Score; var colorGrid = Game.Grid.CGrid; for (var x = 0; x < Game.Grid.MaxGrid.X + 1; x++) { + var startX = x * Multiplier; + var endX = startX + Multiplier; + for (var y = 0; y < Game.Grid.MaxGrid.Y + 1; y++) { - if (colorGrid[x, y] == Color.Empty) continue; - - var startX = x * Multiplier; var startY = y * Multiplier; - _writeableBitmap.FillRectangle(startX, startY, startX + Multiplier, startY + Multiplier, colorGrid[x, y].ToArgb()); + var endY = startY + Multiplier; + + _writeableBitmap.DrawLine(startX, startY, endX, startY, _colorLine); + _writeableBitmap.DrawLine(startX, startY, startX, endY, _colorLine); + if (colorGrid[x, y] == Color.Empty) continue; + _writeableBitmap.FillRectangle(startX, startY, endX, endY, colorGrid[x, y].ToArgb()); } } @@ -80,17 +107,35 @@ public class GameViewModel : INotifyPropertyChanged _writeableBitmap.FillRectangle(startX, startY, startX + Multiplier, startY + Multiplier, color.ToArgb()); } } - + _writeableBitmap.Unlock(); } private void Update(object? sender, EventArgs eventArgs) { + if (_isPaused) + { + return; + } + Game.CurrentTetrominoe?.GoDown(); - + if (Game.HitBottom()) { - Game.PrintTetrominoe(); + if (_countDown >= 3) + { + Game.PrintTetrominoe(); + _countDown = 0; + } + else + _countDown++; + } + + Game.ClearLine(); + + if (Game.HitTop()) + { + _isPaused = true; } } } \ No newline at end of file diff --git a/Tetris/ViewsModels/ViewModel.cs b/Tetris/ViewsModels/ViewModel.cs index e65e2bd..3fe7583 100644 --- a/Tetris/ViewsModels/ViewModel.cs +++ b/Tetris/ViewsModels/ViewModel.cs @@ -4,11 +4,10 @@ namespace Tetris.ViewsModels; public class ViewModel : INotifyPropertyChanged { - public event PropertyChangedEventHandler PropertyChanged; + public event PropertyChangedEventHandler? PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { - if (PropertyChanged != null) - PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } \ No newline at end of file