AI智能
改变未来

俄罗斯方块 算法设计与实现-C#


俄罗斯方块 原理分析 实现

原理分析算法实现

原理

  1. 绘制区域-画板,将绘制区分为多行,每行多个小的色块区元素;
  2. 移动区域,也为一个绘图区,也包含行和行元素,但移动区域存在着色块;
  3. 移动区域在画板上呈现,记录移动区相对于画板左上角的偏移量决定呈现位置,相当于横纵坐标;
  4. 画板着色,当移动区域向下移动和画板区域由颜色重叠,则将本次移动之前的移动区域在画板上着色。

移动区域

  1. 移动区域分为 正方形、线形、凸形、不稳定形;
  2. 每个移动图形有相同的基类,行数、列数、行偏移量、列偏移量;
  3. 移动图形存在初始着色区。

关键点

图形生成器

  1. 图像生成器中默认初始化各种图形形状,并计算个图形的偏移量;
  2. 当需要生成时,将随机产生一个图形,并克隆产生一个全新对象出来;

碰撞

  1. 移动区域逐步移动;
  2. 每一次移动需要记录当前副本,并计算移动区和画板是否重复;如果着色区重叠则将本次移动之前的移动区域在画板上着色;
  3. 如果发生碰撞,则进行画板的行全着色统计、消除并进行图形下沉。

计算分数和消行

旋转

  1. 需要创建一个新的对象,新对象的行数为旋转对象的列数,新对象行的元素个数为旋转对象行数;
  2. 然后进行行转列着色;

效果


完整源码

开发环境:window10
IDE:Visual Studio 2019
Language:C#
类型:控制台

注:不具备可玩性,UI需要换种呈现方式,晃得眼花,千万别玩-头大

Program.cs 程序入口

using ConsoleApp.Tetris;using System;namespace Tetris{class Program{static void Main(string[] args){Control control = new Control();control.Start();Console.ReadKey();}}}

Control.cs 控制模块

using System;using System.Collections.Generic;using System.Text;using System.Threading.Tasks;namespace ConsoleApp.Tetris{public enum State{NotStarted = 0,Starting = 1,Pause = 2,GameOver = 10}public class Control{public static string Key_Left = \"4\";//左移public static string Key_Right = \"6\";//右移public static string Key_Rotate = \"8\";//旋转public static string Key_Down = \"2\";//向下public static string Key_Pause = \"0\";//暂停private Graph graphArea;private GraphFactory factory;private Graph currentMoveArea = null;private Graph lastValidArea = null;private int DefaulTurnTime = 200;private int CurrentTurnTime = 200;private string key = \"\";private  Graph validArea = null;private State state;public Control(){graphArea = new Graph(12, 20);factory = new GraphFactory(graphArea);}public void Start(){state = State.Starting;//刷新画板区域RefreshGraphArea();while (true){//监听按键MonitorKey();//处理画板移动区域HandleGraphMoveArea();//检测结束if (state == State.GameOver)break;//处理画板 结算或移动继续移动HandleGraphArea();Task.Delay(CurrentTurnTime).Wait();}}/// <summary>/// 定时绘制画板/// </summary>private void RefreshGraphArea(){Task.Run(() => {while (true){if (state == State.Starting){if (validArea != null)validArea.Draw();elsegraphArea.Draw();}Task.Delay(CurrentTurnTime).Wait();if (graphArea == null)break;}});}/// <summary>/// 监听按键/// </summary>private void MonitorKey(){while (Console.KeyAvailable){var input = Console.ReadKey(true);key = input.KeyChar.ToString();}}/// <summary>/// 处理移动区域/// </summary>private void HandleGraphMoveArea(){//如果移动区域不存在就创建一个信息if (currentMoveArea == null){CreateMoveArea();}//如果移动区域存在则移动else{MoveArea();}}/// <summary>/// 创建新的移动区域/// </summary>private void CreateMoveArea(){currentMoveArea = factory.CreateArea();validArea = graphArea.DrawOtherGraph(currentMoveArea);//如果创建出来的图形 在画板中无法绘画 则结束if (validArea == null){Console.WriteLine(\"game over\");state = State.GameOver;}}/// <summary>/// 移动处理/// </summary>private void MoveArea(){if (key == Key_Pause){Pause();}if (state != State.Pause){if (currentMoveArea != null){currentMoveArea.RowOffset += 1;}if (key == Key_Left){MoveLeft();}else if (key == Key_Right){MoveRight();}else if (key == Key_Rotate){Rotate();}else if (key == Key_Down){ChangeMoveSpeed();}validArea = graphArea.DrawOtherGraph(currentMoveArea);}}/// <summary>/// 暂停/// </summary>private void Pause(){if (state != State.GameOver)state = state == State.Pause ? State.Starting : State.Pause;key = \"\";}/// <summary>/// 改变移动(下降)速度/// </summary>private void ChangeMoveSpeed(){CurrentTurnTime = CurrentTurnTime / 2;key = \"\";}/// <summary>///  每轮移动后 统计画板/// </summary>private void HandleGraphArea(){if (validArea != null){//移动区域移动后整个区域有效则记录有效区域,作为碰撞后结算依据lastValidArea = validArea;}else{Settlement();}}/// <summary>/// 碰撞 结算/// </summary>private void Settlement(){graphArea = lastValidArea;currentMoveArea = null;if (graphArea.RemoveRowAllColor())graphArea.Draw();CurrentTurnTime = DefaulTurnTime;}private void Rotate(){var temp = currentMoveArea.DeepClone();currentMoveArea = currentMoveArea.Rotate();if (currentMoveArea == null)currentMoveArea = temp;else{var moveArea = graphArea.DrawOtherGraph(currentMoveArea);if (moveArea == null)currentMoveArea = temp;}key = \"\";}private void MoveRight(){currentMoveArea.ColumnOffset++;if (currentMoveArea.ColumnOffset + currentMoveArea.ColumnNumber >= graphArea.ColumnNumber)currentMoveArea.ColumnOffset = graphArea.ColumnNumber - currentMoveArea.ColumnNumber;var moveArea = graphArea.DrawOtherGraph(currentMoveArea);if (moveArea == null)currentMoveArea.ColumnOffset--;key = \"\";}private void MoveLeft(){currentMoveArea.ColumnOffset--;if (currentMoveArea.ColumnOffset < 0)currentMoveArea.ColumnOffset = 0;var moveArea = graphArea.DrawOtherGraph(currentMoveArea);if (moveArea == null)currentMoveArea.ColumnOffset++;key = \"\";}}}

Graph.cs 图案文件

using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Runtime.Serialization.Formatters.Binary;namespace ConsoleApp.Tetris{public class GraphFactory{public Graph[] GraphProducts  { get; set; }public Graph MainArea { get; set; }public GraphFactory(Graph area){MainArea = area;GraphProducts = new Graph[]{new Line() { ColumnOffset = area.ColumnNumber / 2 },new Instability(){ ColumnOffset = area.ColumnNumber / 2 },new Sharp(){ ColumnOffset = area.ColumnNumber / 2 },new Square () { ColumnOffset = area.ColumnNumber / 2 }};}public Graph CreateArea(){byte[] buffer = Guid.NewGuid().ToByteArray();int iRoot = BitConverter.ToInt32(buffer, 0);Random random = new Random(iRoot);int index = random.Next(0, GraphProducts.Length);return GraphProducts[index].DeepClone();}}/// <summary>/// 口/// 口/// 口/// 口/// </summary>[Serializable]public class Line : Graph{public const int ROW = 4;public const int COLUMN = 1;public Line() : base(COLUMN, ROW){foreach (var row in Rows){foreach (var element in row.Elements){element.IsColor = true;element.Color = ConsoleColor.Yellow;}}}}/// <summary>///   口/// 口口/// 口/// </summary>[Serializable]public class Instability : Graph{public const int ROW = 3;public const int COLUMN = 2;public Instability() : base(COLUMN, ROW){foreach (var row in Rows){var rowIndex = Rows.IndexOf(row);foreach (var element in row.Elements){var columnIndex = row.Elements.IndexOf(element);if (rowIndex == 0){if (columnIndex == 1)element.IsColor = true;}else if (rowIndex == 1){element.IsColor = true;}else{if (columnIndex == 0)element.IsColor = true;}element.Color = ConsoleColor.Green;}}}}/// <summary>///   口/// 口口口/// </summary>[Serializable]public class Sharp : Graph{public const int ROW = 2;public const int COLUMN = 3;public Sharp() : base(COLUMN, ROW){foreach (var row in Rows){var rowIndex = Rows.IndexOf(row);foreach (var element in row.Elements){var columnIndex = row.Elements.IndexOf(element);if (rowIndex == 0 && columnIndex == COLUMN / 2){element.IsColor = true;}else if (rowIndex == 1){element.IsColor = true;}element.Color = ConsoleColor.Red;}}}}/// <summary>/// 田/// </summary>[Serializable]public class Square : Graph{public const int ROW = 2;public const int COLUMN = 2;public Square() : base(COLUMN, ROW){foreach (var row in Rows){foreach (var element in row.Elements){element.IsColor = true;element.Color = ConsoleColor.White;}}}}[Serializable]public class Graph{public const string GRAGH_DISPAY = \"口\";public int ColumnOffset { get; set; }public int RowOffset { get; set; }public int ColumnNumber { get; set; }public int RowNumber { get; set; }public List<RowElement> Rows { get; set; }//移除行的数量public int RemoveRow { get; set; } = 0;public Graph(int columnNumber, int rowNumber){ColumnOffset = 0;RowOffset = 0;ColumnNumber = columnNumber;RowNumber = rowNumber;Init();}public void Draw(){Console.Clear();for (int column = 0; column <= ColumnNumber; column++)Console.Write(\"--\");Console.WriteLine();for (int row = 0; row < RowNumber; row++){Console.Write(\"|\");var rowElement = Rows[row];for (int column = 0; column < ColumnNumber; column++){var element = rowElement.Elements[column];if (element.IsColor){Console.ForegroundColor = element.Color;Console.Write(GRAGH_DISPAY);Console.ResetColor();}elseConsole.Write(\"  \");}Console.Write(\"|\");Console.WriteLine();}for (int column = 0; column <= ColumnNumber; column++)Console.Write(\"--\");Console.WriteLine($\"得分:{RemoveRow}\");}private void Init(){Rows = new List<RowElement>();for (int row = 0; row < RowNumber; row++){RowElement rowElement = CreateRow();Rows.Add(rowElement);}}private RowElement CreateRow(){var rowElement = new RowElement(ColumnNumber);for (int column = 0; column < ColumnNumber; column++){rowElement.Elements.Add(new Element());}return rowElement;}/// <summary>/// 将一个图形 画到当前图形上/// </summary>/// <param name=\"otherGraph\"></param>/// <returns>新的图形,如果有色块重叠则null</returns>public Graph DrawOtherGraph(Graph otherGraph){Graph offsetArea = this.DeepClone();for (int row = 0; row < otherGraph.RowNumber; row++){if (otherGraph.RowOffset < 0 || otherGraph.RowOffset + row >= RowNumber)return null;var offsetRow = offsetArea.Rows[otherGraph.RowOffset + row];var areaRow = otherGraph.Rows[row];for (int column = 0; column < otherGraph.ColumnNumber; column++){if (otherGraph.ColumnOffset < 0 || otherGraph.ColumnOffset + column >= ColumnNumber)return null;var offsetColumnElement = offsetRow.Elements[otherGraph.ColumnOffset + column];var areaRowColumnElement = areaRow.Elements[column];if (offsetColumnElement.IsColor && areaRowColumnElement.IsColor){return null;}else{if (areaRowColumnElement.IsColor){offsetColumnElement.IsColor = areaRowColumnElement.IsColor;offsetColumnElement.Color = areaRowColumnElement.Color;}}}}return offsetArea;}/// <summary>/// 旋转/// </summary>/// <param name=\"isRight\"></param>/// <returns></returns>public virtual Graph Rotate(bool isRight = true){//重新初始化一个图形Graph graph = new Graph(this.RowNumber, this.ColumnNumber);graph.ColumnOffset = ColumnOffset;graph.RowOffset = RowOffset;for (int i = 0; i < Rows.Count; i++){var row = Rows[i];for (int j = 0; j < row.Elements.Count; j++){var element = row.Elements[j];var graphElement = graph.Rows[j].Elements[i];if (isRight)graphElement = graph.Rows[j].Elements[graph.ColumnNumber - i - 1];graphElement.IsColor = element.IsColor;graphElement.Color = element.Color;}}return graph;}/// <summary>/// 移除全为颜色的行/// </summary>/// <returns></returns>public bool RemoveRowAllColor(){List<int> removeIndex = new List<int>();for (int row = RowNumber - 1; row >= 0; row--){var rowElement = Rows[row];if (!rowElement.Elements.ToList().Any(column => column.IsColor == false)){RemoveRow++;rowElement.IsAllColor = true;removeIndex.Add(row);}}Graph area = new Graph(ColumnNumber, RowNumber);int rowIndex = RowNumber - 1;for (int row = RowNumber - 1; row > 0; row--){var rowElement = Rows[row];if (!rowElement.IsAllColor){area.Rows[rowIndex--] = rowElement;}if (row == 1 && rowIndex &l4000t; RowNumber - 1){area.Rows[0] = CreateRow();}}Rows = area.Rows;return rowIndex < RowNumber - 1;}public Graph DeepClone(){using (Stream stream = new MemoryStream()){var binaryFormatter = new BinaryFormatter();binaryFormatter.Serialize(stream, this);stream.Seek(0, SeekOrigin.Begin);return binaryFormatter.Deserialize(stream) as Graph;}}}[Serializable]public class RowElement{public int ColumnNumber { get; set; }public List<Element> Elements { get; set; }public bool IsAllColor { get; set; } = false;public RowElement(int rowNumber){ColumnNumber = rowNumber;Elements = new List<Element>();}}[Serializable]public class Element{public bool IsColor { get; set; }public ConsoleColor Color { get; set; }}}
赞(0) 打赏
未经允许不得转载:爱站程序员基地 » 俄罗斯方块 算法设计与实现-C#