C# 飞行棋小游戏 (控制台应用)

C# 控制台飞行棋小游戏

简要介绍

这是我学习C#期间写的一个小项目,和普通的飞行棋类似,但是拥有更加美观的画面(对于控制台应用来说)。游戏的玩家数量为2,通过掷骰子(当然不是真的骰子)来确定玩家移动步数以及随机效果的选择,率先抵达终点的玩家获胜,过程中可以通过使用道具来展现一定的策略,是时候来一场运气与策略的比拼了。。。咳咳, 升华不起来,就这样吧。

游戏画面

开始画面
地图选择以及起始玩家确定
游戏中画面
结束结算画面

规则说明

游戏开始时或通过掷骰子来确定先手玩家,一般情况下玩家回合将轮流进行,玩家停留在地图上的特殊区域时将获得不同效果,每局游戏地图上的特殊区域位置均为随机生成,数量根据地图大小会有不同设置,每回合通过掷骰子来确定玩家前进步数,只有当玩家停在特殊区域才会触发区域效果,单纯的经过是不会触发的。特殊区域的图例会在游戏画面中显示,下面对地图特殊区域进行的效果进行说明。

地雷 :使玩家后退6格,触发后将从地图上删除,走在前面的玩家要小心了
障碍 :使玩家暂停一回合,触发后不会从地图上删除,后面不做特殊说明均为不删除
重掷 :重新投掷一次骰子
道具使用 :每回合开始时每个玩家将获得相同种类和数量的道具,这些道具只能当玩家处于道具使用点时才可使用。道具分为 地雷障碍幸运骰子虫洞闭锁器 四种,其中地雷和障碍根据地图格子位置进行放置(玩家位置会在游戏界面显示,不要数错格子哦~),要注意自己放置的道具也可能会伤害自己;幸运骰子使用后将立即获得一次投掷骰子的机会,骰子点数可以在1-6之间选择;虫洞闭锁器可以关闭地图上的任意一个虫洞,是让自己免于被传送回起点还是过河拆桥就看你的策略了,至于虫洞的介绍嘛…在下面呢。
随机效果 :当玩家停在随机效果区域,将会从 空白地雷障碍重掷道具补给 六中效果中随机选择一种并生效,空白即为无任何效果;地雷、障碍和重掷和前面说明的效果相同;道具即为获得道具使用权;补给即为在可用道具(除虫洞闭锁器以外)中随机获得一种,数量+1。
虫洞 :游戏开始时将会根据地图大小随机生成确定数量的虫洞,玩家停在虫洞位置会被随机传送到其他虫洞之一的位置,虫洞的编号依据虫洞的位置顺序从1开始依次增加。

地图大小:
小地图——100格
中地图——140格
大地图——180格

地图大小对应区域数量表:

地图大小
地雷 10 14 18
障碍 5 7 9
重掷 5 7 9
道具使用 10 14 18
随机效果 10 14 18
虫洞 3 4 5

作弊模式
为了方便测试,我在游戏里加了debug模式,开启方法为:在$Main > 处输入 debug enable 即可开启debug模式。
·
操作指令如下:
debug move <player> <index> 移动玩家到指定位置
debug setmap <index> <gridType> 设置地图指定区域的类型
debug setprop <player> <propType> <value> 设置玩家道具数量
·
<index> 即为地图位置编号,移动玩家位置时范围由1到地图大小,修改地图区域时不可修改终点处
<player> 可使用 player_aplayer_b 的形式,也可以直接写 ab,大小写任意
<gridType> 可选类型为 emptylandminestoprerollrandompropwormhole 分别对应 空白地雷障碍重掷随机效果道具使用虫洞
<propType> 可选类型为 landmineobstacleluckydicewhlocker 分别对应 地雷障碍幸运骰子虫洞闭锁器
<value> 为设置的数量(0-99之间任意值)

游戏代码

游戏的代码并不是很复杂,仅有四个 .cs 文件,分别为 Entry.csOperate.cs,以及两个实体类Map.csPlayer.cs,其中 Entry.cs 是游戏的启动文件,Operate.cs 用于创建游戏进程。游戏中显示内容的颜色并没有使用C#的标准方法,而是采用了ANSI转义序列,具体可以参考这篇博客:ANSI 转义序列

Entry.cs

namespace FlyChess
{
    
    
	public class GameEntry
	{
    
    
		public static void Main()
		{
    
    
			StartPage();
			Operate Game = new Operate();
			Game.Run();
		}
		private static void StartPage()
		{
    
    
			Console.Clear();
			string line = "+-----------------------------------------------------------------------------------------+";
			string head = "" +
				$"{
      
      line}\n" +
				"|\x1b[1;31m   0 000 0   0        0     0            000 0   0     0   0 000 0     00 0      00 0   \x1b[0m |\n" +
				"|\x1b[1;32m   0         0         0   0           0         0     0   0         0         0        \x1b[0m |\n" +
				"|\x1b[1;33m   0         0          0 0           0          0     0   0         0         0        \x1b[0m |\n" +
				"|\x1b[1;34m   0 000 0   0           0            0          0 000 0   0 000 0    0 0 0     0 0 0   \x1b[0m |\n" +
				"|\x1b[1;35m   0         0           0            0          0     0   0               0         0  \x1b[0m |\n" +
				"|\x1b[1;36m   0         0           0             0         0     0   0               0         0  \x1b[0m |\n" +
				"|\x1b[1;37m   0         0 000 0     0               000 0   0     0   0 000 0    0 00      0 00    \x1b[0m |\n" +
				$"{
      
      line}\n";
			Console.Write(head);
			Console.WriteLine("\n\n\x1b[5;34m                            >> ENTER ANY KEY TO START <<\x1b[0m");
			Console.WriteLine("\x1b[11;1H");
			Console.ReadLine();
			Console.Clear();
		}
	}
}

Operate.cs

using FlyChess.Resources;
using System.Text.RegularExpressions;

namespace FlyChess
{
    
    
	internal class Operate
	{
    
    
		#region 数据成员声明与定义

		/// <summary>
		/// 消息等级
		/// </summary>
		private enum MsgType
		{
    
    
			Info, Warning, Error
		}

		/// <summary>
		/// 游戏模式
		/// </summary>
		public enum GameMode
		{
    
    
			Normal_MinMap = 1, Normal_MedMap, Normal_MaxMap
		}

		private Map Map;
		private Player Player_A;
		private Player Player_B;
		private GameMode Mode;

		private int OldValue_A {
    
     get; set; }
		private int OldValue_B {
    
     get; set; }
		private int Location {
    
     get; set; }
		private bool Stop_A {
    
     get; set; }
		private bool Stop_B {
    
     get; set; }
		private bool RoundChange {
    
     get; set; }

		private int RoundOwnership {
    
     get; set; }
		private int Winner {
    
     get; set; }
		private bool DebugAvailable {
    
     get; set; }

		const int RANDNUMFLUSHCOUNT = 21;
		const int MESSAGEWAITTIME = 800;
		const int RANDEVENTSLINECOUNT = 3;

		private int RoundCount {
    
     get; set; }
		private int[] PropCannotAdd {
    
     get; set; } = new int[]
		{
    
    
			(int)Player.Prop.WormholeLocker
		};

		#endregion

		#region 对外接口

		/// <summary>
		/// 游戏开始时的构造方法
		/// </summary>
		public Operate()
		{
    
    
			Map = new Map();
		}

		/// <summary>
		/// 游戏启动
		/// </summary>
		public void Run()
		{
    
    
			// 新游戏调用
			GameInit();
			// 读取存档时调用(未实现)
			// ReadSave();
			string inp;
			while (true)
			{
    
    
				if (CheckGameover())
				{
    
    
					ShowEndMessage();
					break;
				}
				else
				{
    
    
					Console.Write("\n\x1b[1;34m$\x1b[0mMain > ");
					inp = Console.ReadLine();
					if (inp.ToLower() == "x") break;
					if (inp == "debug available" | inp == "debug enable")
					{
    
    
						DebugAvailable = true;
						Console.WriteLine("\x1b[1;32mSuccess\x1b[0m: Debug mode enable");
						Thread.Sleep(800);
						Flush();
					}
					else if (!DebugAvailable & inp != "")
					{
    
    
						Console.WriteLine("\x1b[1;31mError\x1b[0m: Invalid statement");
						Thread.Sleep(800);
						Flush();
					}
					else if (DebugAvailable & inp.Split(" ")[0] == "debug")
					{
    
    
						Debug(inp);
						Flush();
						Map.UpdateGridCount();
						continue;
					}
					else if (inp.ToLower() == "s")
					{
    
    
						Console.WriteLine("\x1b[1;34mInfo\x1b[0m: Stay tuned for archiving");
						Console.ReadLine();
					}
					else
					{
    
    
						Flush();
						NextRound();
						Map.UpdateGridCount();
					}
				}
			}
		}

		/// <summary>
		/// 初始化游戏
		/// <para>选择地图大小 -> 初始化地图 -> 确认先手玩家 -> 输出初始信息</para>
		/// </summary>
		public void GameInit()
		{
    
    
			string inp;
			string[] keysAvailable = new string[] {
    
     "1", "2", "3" };
			Operate.GameMode mode = Operate.GameMode.Normal_MinMap;

			bool initConfirm = false;

			Console.WriteLine("\x1b[1;34m[Normal : 1]  [Medium : 2]  [ProMax : 3]  \x1b[1;31m[Exit : x]\x1b[0m");
			while (true)
			{
    
    
				Console.Write("Select the map size level > \x1b[1;34m");
				inp = Console.ReadLine();
				Console.Write("\x1b[0m");
				if (inp.ToLower() == "x") break;
				else if (keysAvailable.Contains(inp))
				{
    
    
					switch (inp)
					{
    
    
						case "1": mode = Operate.GameMode.Normal_MinMap; break;
						case "2": mode = Operate.GameMode.Normal_MedMap; break;
						case "3": mode = Operate.GameMode.Normal_MaxMap; break;
					}
					Map.InitMap(mode);
					Mode = mode;
					initConfirm = true;
					break;
				}
				else
				{
    
    
					Console.WriteLine("\x1b[1;31mError\x1b[0m: Key not found");
				}
			}
			if (initConfirm)
			{
    
    
				Player_A = new Player(Mode);
				Player_B = new Player(Mode);

				OldValue_A = 2;
				OldValue_B = 1;
				Location = 0;
				Stop_A = false;
				Stop_B = false;
				RoundChange = true;
				Winner = 0;
				DebugAvailable = false;
				RoundCount = 0;

				Console.WriteLine();
				Wait("Map initialization confirm", MsgType.Info, 600);
				Wait("Start identifying the first player", time: 600);
				Console.WriteLine("\nIf the result is \x1b[1;34m1,3,5\x1b[0m then the first player is \x1b[1;34mPlayer_A\x1b[0m,\nOtherwise \x1b[1;34mPlayer_B\x1b[0m is the first player\n");
				Console.Write("Enter any key to start rolling >");
				Console.ReadLine();
				int ret = GetRandomNumber(1, 6);
				if (ret % 2 == 0)
				{
    
    
					RoundOwnership = 2;
					Console.WriteLine("\x1b[1;34mInfo\x1b[0m: The first player is \x1b[1;34mPlayer_B\x1b[0m");
				}
				else
				{
    
    
					RoundOwnership = 1;
					Console.WriteLine("\x1b[1;34mInfo\x1b[0m: The first player is \x1b[1;34mPlayer_A\x1b[0m");
				}
				Console.WriteLine();
				Console.Write("Enter any key to start the game >");
				Console.ReadLine();
				Wait("Game starting", MsgType.Info);
				Thread.Sleep(500);
				Flush();
			}
		}

		/// <summary>
		/// 执行下一回合
		/// </summary>
		public void NextRound()
		{
    
    
			RoundCount++;
			int ret;
			bool judge = true;
			if (RoundOwnership == 1)
			{
    
    
				if (CheckRoundStop()) {
    
     judge = false; Console.WriteLine("\x1b[1;34mInfo\x1b[0m:	Player_A Stopped"); Console.Write("Continue > "); Console.ReadLine(); Flush(); }
				else
				{
    
    
					Console.WriteLine("\x1b[1;34mInfo\x1b[0m: Player_A rolling the dice");
					ret = GetRandomNumber(1, 6);
					Console.Write("Continue > ");
					Console.ReadLine();
					StructuralFrame(Player_A.Location, Player_A.Location + ret);
				}
			}
			else
			{
    
    
				if (CheckRoundStop()) {
    
     judge = false; Console.WriteLine("\x1b[1;34mInfo\x1b[0m:	Player_B Stopped"); Console.Write("Continue > "); Console.ReadLine(); Flush(); }
				else
				{
    
    
					Console.WriteLine("\x1b[1;34mInfo\x1b[0m: Player_B rolling the dice");
					ret = GetRandomNumber(1, 6);
					Console.Write("Continue > ");
					Console.ReadLine();
					StructuralFrame(Player_B.Location, Player_B.Location + ret);
				}
			}
			if (judge) RulesJudge();
			ChangeRound();
		}

		/// <summary>
		/// 判断游戏是否结束
		/// </summary>
		/// <returns>检测到获胜玩家则返回 true, 否则返回 false</returns>
		public bool CheckGameover()
		{
    
    
			if (Winner == 0) return false;
			else return true;
		}

		#endregion

		#region 私有方法

		/// <summary>
		/// 掷骰子模拟
		/// </summary>
		/// <param name="min">最小值</param>
		/// <param name="max">最大值</param>
		/// <param name="ignore">忽略值</param>
		/// <returns></returns>
		private int GetRandomNumber(int min, int max, int confirm = 0, params int[] ignore)
		{
    
    
			Random random = new Random();
			int[] gets = new int[RANDNUMFLUSHCOUNT];
			int old = 0;
			int get = 0;
			for (int i = 0; i < gets.Length; i++)
			{
    
    
				while (true)
				{
    
    
					get = random.Next(min, max + 1);
					if (get != old & !ignore.Contains(get))
					{
    
    
						gets[i] = get;
						old = get;
						break;
					}
				}
			}
			string show;
			//int len;
			for (int i = 0; i < gets.Length; i++)
			{
    
    
				show = $"Rolling: \x1b[1;34m{
      
      gets[i]}\x1b[0m";
				//len = show.Length;
				if (i != 0) Console.Write("\x1b[1A");//\x1b[{len}D
				Console.WriteLine($"{
      
      show}");
				if (i < 5) Thread.Sleep(40);
				else if (i < 10) Thread.Sleep(80);
				else if (i < 14) Thread.Sleep(130);
				else if (i < 17) Thread.Sleep(190);
				else if (i < 19) Thread.Sleep(260);
				else if (i < 20) Thread.Sleep(400);
			}
			if (confirm != 0) gets[RANDNUMFLUSHCOUNT - 1] = confirm;
			Console.WriteLine($"\x1b[1AResult:  \x1b[1;34m{
      
      gets[RANDNUMFLUSHCOUNT - 1]}\x1b[0m");
			return gets[RANDNUMFLUSHCOUNT - 1];
		}

		/// <summary>
		/// 刷新界面
		/// </summary>
		private void Flush()
		{
    
    
			Console.Clear();
			Console.WriteLine("\x1b[1;31m[ Exit: X ]\x1b[0m  \x1b[1;32m[ Save: S ]\x1b[0m");
			Map.PrintMap();
			if (RoundOwnership == 1)
			{
    
    
				Console.Write("当前玩家: \x1b[1;34mPlayer_A\x1b[0m");
				Console.WriteLine($"                                    玩家位置: [ Player_A: \x1b[1;34m{
      
      Player_A.Location + 1}\x1b[0m ]  [ Player_B: \x1b[1;34m{
      
      Player_B.Location + 1}\x1b[0m ]");
				Player_A.PrintPropMessage();
			}
			else
			{
    
    
				Console.Write("当前玩家: \x1b[1;34mPlayer_B\x1b[0m");
				Console.WriteLine($"                                    玩家位置: [ Player_A: \x1b[1;34m{
      
      Player_A.Location + 1}\x1b[0m ]  [ Player_B: \x1b[1;34m{
      
      Player_B.Location + 1}\x1b[0m ]");
				Player_B.PrintPropMessage();
			}
			Console.WriteLine("+------------------------------------------------------------------------------------------------+");
		}

		/// <summary>
		/// 生成并播放移动帧
		/// </summary>
		/// <param name="startLoca">起始坐标</param>
		/// <param name="stopLoca">结束坐标</param>
		private void StructuralFrame(int startLoca, int stopLoca)
		{
    
    
			int stop;
			int maxIndex = Map.GetMapSize() - 1;
			if (stopLoca > maxIndex) stop = maxIndex;
			else stop = stopLoca;
			int move = stop - startLoca;
			int frameCount = Math.Abs(move);
			int location = startLoca;
			int add;
			if (move < 0) add = -1;
			else add = 1;
			for (int i = 0; i < frameCount; i++)
			{
    
    
				if (RoundOwnership == 1)
				{
    
    
					Map.SetMapShowArray(location, value: OldValue_A);
					location += add;
					OldValue_A = Map.GetMapShowArray(location);
					Map.SetMapShowArray(location, Map.GridType.Player_A);
					Player_A.Location = location;
					Player_A.Step++;
				}
				else
				{
    
    
					Map.SetMapShowArray(location, value: OldValue_B);
					location += add;
					OldValue_B = Map.GetMapShowArray(location);
					Map.SetMapShowArray(location, Map.GridType.Player_B);
					Player_B.Location = location;
					Player_B.Step++;
				}
				CheckDataConsistency(location - add);
				Flush();
				Thread.Sleep(300);
			}
			if (RoundOwnership == 1) {
    
     Player_A.Location = stop; Location = stop; }
			else {
    
     Player_B.Location = stop; Location = stop; }
			CheckPlayersOverlap();
		}

		/// <summary>
		/// 检测玩家重合
		/// </summary>
		private void CheckPlayersOverlap()
		{
    
    
			if (OldValue_A == 2 & Player_A.Location != Player_B.Location) OldValue_A = 0;
			if (OldValue_B == 1 & Player_A.Location != Player_B.Location) OldValue_B = 0;
			if (Player_A.Location == Player_B.Location)
			{
    
    
				OldValue_A = 2;
				OldValue_B = 1;
				Map.SetMapShowArray(Player_A.Location, value: 3);
				Flush();
			}
		}

		/// <summary>
		/// 规则判定
		/// </summary>
		private void RulesJudge()
		{
    
    
			int next = Map.GetMapDataArray(Location);
			while (true)
			{
    
    
				if (next == 0)
				{
    
    
					Console.WriteLine("无事发生");
					break;
				}
				else if (next == 100) break;
				switch (next)
				{
    
    
					case (int)Map.GridType.Landmine:
						next = RLandmine();
						break;
					case (int)Map.GridType.Stop:
						next = RStop();
						break;
					case (int)Map.GridType.Reroll:
						next = RReroll();
						break;
					case (int)Map.GridType.Random:
						next = RRandom();
						break;
					case (int)Map.GridType.Prop:
						next = RProp();
						break;
					case (int)Map.GridType.PropAdd:
						next = RPropAdd();
						break;
					case (int)Map.GridType.Wormhole:
						next = RWormhole();
						break;
					case (int)Map.GridType.Win:
						next = GameOver();
						break;
				}
			}
		}

		/// <summary>
		/// 随机效果生成器
		/// </summary>
		/// <returns>返回随机效果对应的规则代码, 用于后续判断</returns>
		private int GenerateRandomEvent()
		{
    
    
			Random random = new Random();
			string[] randEvents = new string[]
			{
    
    
				"[ 空白 ]","[ 地雷 ]","[ 障碍 ]",
				"[ 重掷 ]","[ 道具 ]","[ 补给 ]"
			};
			Dictionary<int, Map.GridType> eventType = new Dictionary<int, Map.GridType>()
			{
    
    
				{
    
    0, Map.GridType.Empty},
				{
    
    1, Map.GridType.Landmine},
				{
    
    2, Map.GridType.Stop},
				{
    
    3, Map.GridType.Reroll},
				{
    
    4, Map.GridType.Prop},
				{
    
    5, Map.GridType.PropAdd},
			};
			int lineCount = randEvents.Length / RANDEVENTSLINECOUNT;
			string print;
			int[] gets = new int[RANDNUMFLUSHCOUNT];
			int old = 0;
			int get = 0;
			for (int i = 0; i < gets.Length; i++)
			{
    
    
				while (true)
				{
    
    
					get = random.Next(0, 6);
					if (get != old)
					{
    
    
						gets[i] = get;
						old = get;
						break;
					}
				}
			}
			for (int i = 0; i < gets.Length; i++)
			{
    
    
				print = "";
				for (int j = 0; j < randEvents.Length; j++)
				{
    
    
					if (j == gets[i]) print += $"\x1b[1;44m{
      
      randEvents[j]}\x1b[0m";
					else print += $"{
      
      randEvents[j]}";
					if (j != 0 & j != randEvents.Length - 1 & (j + 1) % RANDEVENTSLINECOUNT == 0) print += "\n";
				}
				if (i != 0)
				{
    
    
					for (int j = 0; j < lineCount; j++)
						Console.Write("\x1b[1A");
				}
				Console.WriteLine($"{
      
      print}");
				if (i < 5) Thread.Sleep(40);
				else if (i < 10) Thread.Sleep(80);
				else if (i < 14) Thread.Sleep(130);
				else if (i < 17) Thread.Sleep(190);
				else if (i < 19) Thread.Sleep(260);
				else if (i < 20) Thread.Sleep(400);
			}
			Console.WriteLine($"随机效果: {
      
      randEvents[get]}");
			Console.Write("Continue > ");
			Console.ReadLine();
			Flush();
			return (int)eventType[get];
		}

		/// <summary>
		/// 道具放置
		/// </summary>
		/// <param name="p">道具种类</param>
		/// <returns>返回结束代码, 服务于使玩家位置移动的道具效果结束时的判定, 返回 0 表示未使用道具</returns>
		private int PlaceProps(Player.Prop p)
		{
    
    
			int ret = 0;
			if (p == Player.Prop.LuckyDice)
			{
    
    
				while (true)
				{
    
    
					Flush();
					Console.Write("[幸运骰子]  输入想要的结果 > ");
					string inp = Console.ReadLine();
					if (inp.ToLower() == "x") break;
					try
					{
    
    
						int loca = Convert.ToInt32(inp);
						if (loca <= 0 | loca > 6) {
    
     Console.WriteLine("\x1b[1;31mError\x1b[0m: Number must between 1 and 6"); Thread.Sleep(500); }
						else
						{
    
    
							GetRandomNumber(1, 6, confirm: loca);
							Console.Write("Continue > ");
							Console.ReadLine();
							StructuralFrame(Location, Location + loca);
							ret = Map.GetMapDataArray(Location);
							if (ret == 0) ret = 100;
							break;
						}
					}
					catch (Exception)
					{
    
    
						Console.WriteLine("\x1b[1;31mError\x1b[0m: Please enter a number");
						Thread.Sleep(500);
					}
				}
			}
			else if (p == Player.Prop.WormholeLocker)
			{
    
    
				while (true)
				{
    
    
					Flush();
					Console.Write("[虫洞锁闭器]  选择需关闭的虫洞编号 > ");
					string inp = Console.ReadLine();
					if (inp.ToLower() == "x") break;
					try
					{
    
    
						int loca = Convert.ToInt32(inp);
						if (loca < 1 | loca > Map.GetWormholeCount()) {
    
     Console.WriteLine("\x1b[1;31mError\x1b[0m: Location out of range"); Thread.Sleep(500); }
						else
						{
    
    
							Map.SetMapDataArray(Map.WormHoleIndex[loca], type: Map.GridType.Empty);
							CheckDataConsistency(Map.WormHoleIndex[loca]);
							Map.UpdateWormholeIndex();
							Map.SetWormholeCount(Map.GetWormholeCount() - 1);

							ret = 100;
							break;
						}
					}
					catch (Exception)
					{
    
    
						Console.WriteLine("\x1b[1;31mError\x1b[0m: Please enter a number");
						Thread.Sleep(500);
					}
				}
			}
			else
			{
    
    
				Map.GridType toPlace = Map.GridType.Empty;
				string tip = "";
				switch (p)
				{
    
    
					case Player.Prop.Landmine:
						toPlace = Map.GridType.Landmine;
						tip = "设置地雷";
						break;
					case Player.Prop.Obstacle:
						toPlace = Map.GridType.Stop;
						tip = "设置障碍";
						break;
				}
				while (true)
				{
    
    
					Flush();
					Console.Write($"[{
      
      tip}]  输入放置位置 > ");
					string inp = Console.ReadLine();
					if (inp.ToLower() == "x") break;
					try
					{
    
    
						int loca = Convert.ToInt32(inp);
						if (loca < 1 | loca > Map.GetMapSize()) {
    
     Console.WriteLine("\x1b[1;31mError\x1b[0m: Location out of range"); Thread.Sleep(500); }
						else
						{
    
    
							if (Map.GetMapDataArray(loca - 1) != 0 | (loca - 1) == Player_A.Location | (loca - 1) == Player_A.Location)
							{
    
    
								Console.WriteLine("\x1b[1;31mError\x1b[0m: Location not empty, can't place there");
								Thread.Sleep(500);
							}
							else
							{
    
    
								Map.SetMapDataArray(loca - 1, type: toPlace);
								Map.SetMapShowArray(loca - 1, type: toPlace);
								ret = 100;
								break;
							}
						}
					}
					catch (Exception)
					{
    
    
						Console.WriteLine("\x1b[1;31mError\x1b[0m: Please enter a number");
						Thread.Sleep(500);
					}
				}
			}
			return ret;
		}

		/// <summary>
		/// 输出游戏结束时的信息
		/// </summary>
		private void ShowEndMessage()
		{
    
    
			Console.Clear();
			string line = "+-----------------------------------------------------------------------------------------+";
			string head = "" +
				$"{
      
      line}\n" +
				"|\x1b[1;34m    0000       00        00   00     0 000 0  \x1b[1;31m      000    0      0  0 000 0  0 0000    \x1b[0m |\n" +
				"|\x1b[1;34m  0      0    0  0      0  0 0  0    0        \x1b[1;31m     0   0   0      0  0        0     0   \x1b[0m |\n" +
				"|\x1b[1;34m 0           0    0    0    0    0   0        \x1b[1;31m    0     0  0      0  0        0      0  \x1b[0m |\n" +
				"|\x1b[1;34m 0   00000  0 0000 0  0     0     0  0 000 0  \x1b[1;31m    0     0  0      0  0 000 0  0 000 0   \x1b[0m |\n" +
				"|\x1b[1;34m 0   0   0  0      0  0           0  0        \x1b[1;31m    0     0   0    0   0        0  0      \x1b[0m |\n" +
				"|\x1b[1;34m  0     0   0      0  0           0  0        \x1b[1;31m     0   0     0  0    0        0   0     \x1b[0m |\n" +
				"|\x1b[1;34m    000     0      0  0           0  0 000 0  \x1b[1;31m      000       00     0 000 0  0    00   \x1b[0m |\n" +
				$"{
      
      line}\n";

			string winner;
			if (Winner == 1) winner = "Player_A";
			else winner = "Player_B";

			string[] message = new string[] {
    
    
				"",
				$"Winner:\x1b[1;31m{
      
      winner}\x1b[0m",
				"",
				$"Round Count:\x1b[1;34m{
      
      RoundCount}\x1b[0m",
				//$"Distance:\x1b[1;34m{Math.Abs(Player_A.Location-Player_B.Location)}\x1b[0m",
				"",
				"[Player_A]                                 [Player_B]",
				$"Step:\x1b[1;34m{
      
      Player_A.Step}\x1b[0m"                                      +"|"+     $"Step:\x1b[1;34m{
      
      Player_B.Step}\x1b[0m",
				$"Landmine:\x1b[1;34m{
      
      Player_A.Experience[Map.GridType.Landmine]}\x1b[0m"     +"|"+     $"Landmine:\x1b[1;34m{
      
      Player_B.Experience[Map.GridType.Landmine]}\x1b[0m",
				$"Prop:\x1b[1;34m{
      
      Player_A.Experience[Map.GridType.Prop]}\x1b[0m"             +"|"+     $"Prop:\x1b[1;34m{
      
      Player_B.Experience[Map.GridType.Prop]}\x1b[0m",
				$"PropAdd:\x1b[1;34m{
      
      Player_A.Experience[Map.GridType.PropAdd]}\x1b[0m"       +"|"+     $"PropAdd:\x1b[1;34m{
      
      Player_B.Experience[Map.GridType.PropAdd]}\x1b[0m",
				$"Random:\x1b[1;34m{
      
      Player_A.Experience[Map.GridType.Random]}\x1b[0m"         +"|"+     $"Random:\x1b[1;34m{
      
      Player_B.Experience[Map.GridType.Random]}\x1b[0m",
				$"Reroll:\x1b[1;34m{
      
      Player_A.Experience[Map.GridType.Reroll]}\x1b[0m"         +"|"+     $"Reroll:\x1b[1;34m{
      
      Player_B.Experience[Map.GridType.Reroll]}\x1b[0m",
				$"Stop:\x1b[1;34m{
      
      Player_A.Experience[Map.GridType.Stop]}\x1b[0m"             +"|"+     $"Stop:\x1b[1;34m{
      
      Player_B.Experience[Map.GridType.Stop]}\x1b[0m",
				$"Wormhole:\x1b[1;34m{
      
      Player_A.Experience[Map.GridType.Wormhole]}\x1b[0m"     +"|"+     $"Wormhole:\x1b[1;34m{
      
      Player_B.Experience[Map.GridType.Wormhole]}\x1b[0m",
				"",
				"",
				">> Enter Any Key To Continue <<",
			};

			int spacenum = line.Length / 2;
			Regex regex = new Regex(@"\x1b\[.*?m");
			string print;
			string l1;
			string l2;
			string r1;
			string r2;

			Console.Write(head);
			foreach (string msg in message)
			{
    
    
				if (msg == "") Console.WriteLine();
				else if (msg.Split(":").Length != 2)
				{
    
    
					if (msg.Split("|").Length == 1) Console.WriteLine("\x1b[1;32m" + msg.PadLeft(spacenum + (msg.Length / 2)) + "\x1b[0m");
					else
					{
    
    
						l1 = msg.Split("|")[0].Split(":")[0];
						l2 = msg.Split("|")[0].Split(":")[1];
						r1 = msg.Split("|")[1].Split(":")[0];
						r2 = msg.Split("|")[1].Split(":")[1];
						print = l1.PadLeft(spacenum / 2) + " : " + l2 + " ".PadRight(26 - l2.Length) + "   " + r1.PadLeft(spacenum / 2) + " : " + r2;
						Console.WriteLine(print);
					}
				}
				else
				{
    
    
					print = regex.Replace(msg.Split(":")[0], "").PadLeft(spacenum - 1) + " : " + msg.Split(":")[1];
					Console.WriteLine(print);
				}
			}
			Console.ReadLine();
		}

		#endregion

		#region 工具方法

		/// <summary>
		/// 消息等待
		/// <para>time 为时间, 200 的整倍数为精确输出</para>
		/// </summary>
		/// <param name="msg">消息内容</param>
		/// <param name="type">消息等级</param>
		/// <param name="time">时间(毫秒, 200整倍数精确)</param>
		private void Wait(string msg, MsgType type = MsgType.Info, int time = 1000)
		{
    
    
			string o_type = "";
			int count = 0;
			switch (type)
			{
    
    
				case MsgType.Info: o_type = "Info"; break;
				case MsgType.Warning: o_type = "Warning"; break;
				case MsgType.Error: o_type = "Error"; break;
			}
			count = time / 200;
			Console.Write($"\x1b[1;34m{
      
      o_type}\x1b[0m: {
      
      msg} ");
			for (int i = 0; i < count; i++)
			{
    
    
				Thread.Sleep(200);
				Console.Write(".");
			}
			Console.WriteLine();
		}

		/// <summary>
		/// 切换回合所有权
		/// </summary>
		private void ChangeRound()
		{
    
    
			if (RoundChange)
			{
    
    
				if (RoundOwnership == 1) RoundOwnership = 2;
				else RoundOwnership = 1;
			}
			else
			{
    
    
				RoundChange = true;
			}
		}

		/// <summary>
		/// 清除当前位置的旧值, 用于清除一次性单元
		/// </summary>
		private void DeleteGrid(int location)
		{
    
    
			if (RoundOwnership == 1) OldValue_A = 0;
			else OldValue_B = 0;
			if (Map.GetMapDataArray(location) != (int)Map.GridType.Random)
			{
    
    
				Map.SetMapShowArray(location, Map.GridType.Empty);
				Map.SetMapDataArray(location, Map.GridType.Empty);
			}
		}

		/// <summary>
		/// 检查回合是否停滞
		/// </summary>
		/// <returns>停滞返回true, 否则返回false</returns>
		private bool CheckRoundStop()
		{
    
    
			if (RoundOwnership == 1 & Stop_A == true) {
    
     Stop_A = false; return true; }
			else if (RoundOwnership == 2 & Stop_B == true) {
    
     Stop_B = false; return true; }
			else return false;
		}

		/// <summary>
		/// 检查显示数组数据是否与数据数组是否一致, 若不一致则更改显示数组
		/// </summary>
		/// <param name="location">检查位置</param>
		private void CheckDataConsistency(int location)
		{
    
    
			int show = Map.GetMapShowArray(location);
			int data = Map.GetMapDataArray(location);
			if (show != data & show != (int)Map.GridType.Player_A & show != (int)Map.GridType.Player_B & show != (int)Map.GridType.Player_Both)
			{
    
    
				Map.SetMapShowArray(location, value: Map.GetMapDataArray(location));
			}
		}

		#endregion

		#region 规则方法

		/// <summary>
		/// 遇到地雷时触发的规则
		/// </summary>
		/// <returns>返回移动结束时的单元事件, 用于判断是否再次触发规则</returns>
		private int RLandmine()
		{
    
    
			if (RoundOwnership == 1) Player_A.Experience[Map.GridType.Landmine]++;
			else Player_B.Experience[Map.GridType.Landmine]++;
			int old = Location;
			DeleteGrid(Location);
			Console.WriteLine("踩到地雷, 后退6格");
			Thread.Sleep(MESSAGEWAITTIME);
			if (Location < 6) Location = 0;
			else Location -= 6;
			StructuralFrame(old, Location);
			return Map.GetMapDataArray(Location);
		}

		/// <summary>
		/// 遇到障碍时触发的规则
		/// </summary>
		/// <returns>返回特殊标记: 100, 表示回合结束且不输出信息</returns>
		private int RStop()
		{
    
    
			Console.WriteLine("遇到障碍, 暂停一回合");
			Thread.Sleep(MESSAGEWAITTIME);
			if (RoundOwnership == 1)
			{
    
    
				Stop_A = true;
				Player_A.Experience[Map.GridType.Stop]++;
			}
			else
			{
    
    
				Stop_B = true;
				Player_B.Experience[Map.GridType.Stop]++;
			}
			return 100;
		}

		/// <summary>
		/// 遇到重新抛掷时触发的规则
		/// </summary>
		/// <returns>返回移动结束时的单元事件, 用于判断是否再次触发规则</returns>
		private int RReroll()
		{
    
    
			if (RoundOwnership == 1) Player_A.Experience[Map.GridType.Reroll]++;
			else Player_B.Experience[Map.GridType.Reroll]++;
			Console.WriteLine("重掷骰子");
			Thread.Sleep(MESSAGEWAITTIME);
			int ret = GetRandomNumber(1, 6);
			Console.Write("Continue > ");
			Console.ReadLine();
			StructuralFrame(Location, Location + ret);
			return Map.GetMapDataArray(Location);
		}

		/// <summary>
		/// 遇到随机事件时触发的规则
		/// </summary>
		/// <returns>返回移动结束时的单元事件, 用于判断是否再次触发规则</returns>
		private int RRandom()
		{
    
    
			if (RoundOwnership == 1) Player_A.Experience[Map.GridType.Random]++;
			else Player_B.Experience[Map.GridType.Random]++;
			Console.WriteLine("随机效果");
			Thread.Sleep(MESSAGEWAITTIME);
			int ret = GenerateRandomEvent();
			return ret;
		}

		/// <summary>
		/// 遇到道具使用点时触发的规则
		/// </summary>
		/// <returns>返回移动结束时的单元事件, 用于判断是否再次触发规则</returns>
		private int RProp()
		{
    
    
			if (RoundOwnership == 1) Player_A.Experience[Map.GridType.Prop]++;
			else Player_B.Experience[Map.GridType.Prop]++;
			Console.WriteLine("道具使用点");
			Thread.Sleep(MESSAGEWAITTIME);
			int key = 1;
			List<string> useable = new List<string>();
			string tip = "Menu: ";
			foreach (string pname in Player.PropName)
			{
    
    
				tip += $"\x1b[1;34m[{
      
      pname} : {
      
      key}]\x1b[0m  ";
				useable.Add(key.ToString());
				key++;
			}
			tip += "\x1b[1;31m[Exit : x]\x1b[0m";

			Player.Prop p;
			int ret = 100;
			bool succ;
			while (true)
			{
    
    
				Flush();
				Console.WriteLine(tip);
				Console.Write("Choose > ");
				string inp = Console.ReadLine();
				if (inp.ToLower() == "x") {
    
     Console.WriteLine("放弃使用"); Thread.Sleep(500); break; }
				if (useable.Contains(inp))
				{
    
    
					p = (Player.Prop)(Convert.ToInt32(inp) - 1);
					if (RoundOwnership == 1) succ = Player_A.PropUse(p);
					else succ = Player_B.PropUse(p);
					if (succ)
					{
    
    
						ret = PlaceProps(p);
						if (ret != 0)
						{
    
    
							Flush();
							Console.WriteLine($"\x1b[1;34mInfo\x1b[0m: Prop: \x1b[1;34m{
      
      Player.PropName[(int)p]}\x1b[0m successfully use");
							Console.Write("Continue > ");
							Console.ReadLine();
							break;
						}
						else
						{
    
    
							if (RoundOwnership == 1) Player_A.PCount.Count[p]++;
							else Player_B.PCount.Count[p]++;
							Flush();
							Console.Write("道具未成功使用, 次数已返还\nContinue > ");
							Console.ReadLine();
							ret = 100;
						}
					}
				}
				else
				{
    
    
					Console.WriteLine("\x1b[1;31mError\x1b[0m: Key not found");
				}
				Thread.Sleep(500);
			}
			Flush();
			return ret;
		}

		/// <summary>
		/// 遇到道具补给点时触发的规则
		/// </summary>
		/// <returns>返回特殊标记: 100, 表示回合结束且不输出信息</returns>
		private int RPropAdd()
		{
    
    
			Console.WriteLine("随机选择道具中... [0:地雷  1: 障碍  2: 幸运骰子]");
			int cho = GetRandomNumber(0, 3, ignore: PropCannotAdd);
			Player.Prop p = Player.Prop.Landmine;
			string tip = "";
			switch (cho)
			{
    
    
				case 0:
					p = Player.Prop.Landmine;
					tip = "地雷";
					break;
				case 1:
					p = Player.Prop.Obstacle;
					tip = "障碍";
					break;
				case 2:
					p = Player.Prop.LuckyDice;
					tip = "幸运骰子";
					break;
			}
			if (RoundOwnership == 1)
			{
    
    
				Player_A.Experience[Map.GridType.PropAdd]++;
				Player_A.PropAdd(p, 1);
			}
			else
			{
    
    
				Player_B.Experience[Map.GridType.PropAdd]++;
				Player_B.PropAdd(p, 1);
			}
			Thread.Sleep(800);
			Flush();
			Console.WriteLine($"道具: \x1b[1;34m{
      
      tip}\x1b[0m 可使用次数增加");
			return 100;
		}

		/// <summary>
		/// 遇到虫洞时触发的规则
		/// </summary>
		/// <returns>返回特殊标记: 100, 表示回合结束且不输出信息</returns>
		private int RWormhole()
		{
    
    
			Console.WriteLine("遇到虫洞");
			Thread.Sleep(MESSAGEWAITTIME);
			int nowIndex = 0;
			foreach (KeyValuePair<int, int> kvp in Map.WormHoleIndex)
			{
    
    
				if (kvp.Value == Location) {
    
     nowIndex = kvp.Key; break; }
			}
			int ret = GetRandomNumber(1, Map.GetWormholeCount(), ignore: nowIndex);
			int to = Map.WormHoleIndex[ret];
			Console.WriteLine($"传送到第 \x1b[1;34m{
      
      ret}\x1b[0m 个虫洞的位置, 位置坐标: \x1b[1;34m{
      
      to + 1}\x1b[0m");
			Console.ReadLine();
			if (RoundOwnership == 1)
			{
    
    
				Player_A.Experience[Map.GridType.Wormhole]++;
				Map.SetMapShowArray(to, Map.GridType.Player_A);
				Map.SetMapShowArray(Location, value: Map.GetMapDataArray(Location));
				Player_A.Location = to;
				Location = to;
			}
			else
			{
    
    
				Player_B.Experience[Map.GridType.Wormhole]++;
				Map.SetMapShowArray(to, Map.GridType.Player_B);
				Map.SetMapShowArray(Location, value: Map.GetMapDataArray(Location));
				Player_B.Location = to;
				Location = to;
			}
			CheckPlayersOverlap();
			Flush();
			return 100;
		}

		/// <summary>
		/// 当任意玩家到达终点时触发的规则, 设置获胜方: Winner, 不进行回合交换
		/// </summary>
		/// <returns>返回特殊标记: 100, 表示回合结束且不输出信息</returns>
		private int GameOver()
		{
    
    
			if (RoundOwnership == 1)
			{
    
    
				Console.WriteLine("玩家 A 率先到达终点, 游戏结束");
				Winner = 1;
			}
			else
			{
    
    
				Console.WriteLine("玩家 B 率先到达终点, 游戏结束");
				Winner = 2;
			}
			RoundChange = false;
			return 100;
		}

		#endregion

		#region 测试

		public void Test()
		{
    
    
			//while (true)
			//{
    
    
			//	Map.InitMap(Map.MapSizeLevel.Large);
			//	Console.Clear();
			//	Map.Show();
			//	Map.PrintMap();
			//	Console.ReadLine();
			//}
			//while (true)
			//{
    
    
			//	int ret = GetRandomNumber(1, 6);
			//	Console.WriteLine(ret);
			//	Console.ReadLine();
			//}
			// 新游戏调用
			GameInit();
			// 读取存档时调用
			// ReadSave();
			string inp;
			while (true)
			{
    
    
				if (CheckGameover()) ShowEndMessage();
				else
				{
    
    
					NextRound();
					Console.Write("\n\x1b[1;34m$\x1b[0mMain > ");
					inp = Console.ReadLine();
					if (inp.ToLower() == "x") break;
					else if (inp.ToLower() == "s")
					{
    
    
						Console.WriteLine("\x1b[1;34mInfo\x1b[0m: Stay tuned for archiving");
						Console.ReadLine();
					}
					Flush();
				}
			}
			//while (true)
			//{
    
    
			//	GenerateRandomEvent();
			//}

			//ShowEndMessage();
			//Console.ReadLine();
		}

		/// <summary>
		/// Debug 测试语句
		/// <para>debug move player index 移动玩家到指定位置</para>
		/// <para>debug setmap index grid 设置地图指定位置</para>
		/// </summary>
		/// <param name="dbg_str">操作语句</param>
		public void Debug(string dbg_str)
		{
    
    
			try
			{
    
    
				bool succ = false;
				string[] arr = dbg_str.Split(" ");
				string operation = arr[1].ToLower();
				if (operation == "move")
				{
    
    
					bool changeRound = false;
					string player = arr[2].ToLower();
					int index = Convert.ToInt32(arr[3]);
					if (index > 0 & index <= Map.GetMapSize())
					{
    
    
						if (player == "player_a" | player == "a")
						{
    
    
							if (RoundOwnership != 1)
							{
    
    
								RoundOwnership = 1;
								changeRound = true;
							}
							Map.SetMapShowArray(Player_A.Location, value: OldValue_A);
							OldValue_A = Map.GetMapShowArray(index - 1);
							Map.SetMapShowArray(index - 1, Map.GridType.Player_A);
							Player_A.Location = index - 1;
							Location = index - 1;
						}
						else if (player == "player_b" | player == "b")
						{
    
    
							if (RoundOwnership != 2)
							{
    
    
								RoundOwnership = 2;
								changeRound = true;
							}
							Map.SetMapShowArray(Player_B.Location, value: OldValue_B);
							OldValue_B = Map.GetMapShowArray(index - 1);
							Map.SetMapShowArray(index - 1, Map.GridType.Player_B);
							Player_B.Location = index - 1;
							Location = index - 1;
						}
						else
						{
    
    
							Console.WriteLine("\x1b[1;31mError\x1b[0m: Only have player_a and player_b");
							Console.Write("Continue > ");
							Console.ReadLine();
							return;
						}
						CheckPlayersOverlap();
						Flush();
						RulesJudge();
						if (changeRound)
						{
    
    
							ChangeRound();
						}
						succ = true;
					}
					else
					{
    
    
						Console.WriteLine("\x1b[1;31mError\x1b[0m: Index out of range");
						Console.Write("Continue > ");
						Console.ReadLine();
						return;
					}
				}
				else if (operation == "setmap")
				{
    
    
					int index = Convert.ToInt32(arr[2]);
					string grid = arr[3].ToLower();
					bool locke = false;
					if (index > 0 & index <= Map.GetMapSize() - 1)
					{
    
    
						switch (grid)
						{
    
    
							case "empty": Map.SetMapDataArray(index - 1, Map.GridType.Empty); break;
							case "landmine": Map.SetMapDataArray(index - 1, Map.GridType.Landmine); break;
							case "stop": Map.SetMapDataArray(index - 1, Map.GridType.Stop); break;
							case "reroll": Map.SetMapDataArray(index - 1, Map.GridType.Reroll); break;
							case "random": Map.SetMapDataArray(index - 1, Map.GridType.Random); break;
							case "prop": Map.SetMapDataArray(index - 1, Map.GridType.Prop); break;
							case "wormhole": Map.SetMapDataArray(index - 1, Map.GridType.Wormhole); break;
							default:
								locke = true;
								Console.WriteLine("\x1b[1;31mError\x1b[0m: Grid type not fouund");
								Console.Write("Continue > ");
								Console.ReadLine();
								break;
						}
						CheckDataConsistency(index - 1);
						Map.UpdateWormholeIndex();
						if (!locke) succ = true;
					}
					else
					{
    
    
						Console.WriteLine("\x1b[1;31mError\x1b[0m: Index out of range (1-179)");
						Console.Write("Continue > ");
						Console.ReadLine();
						return;
					}
				}
				else if (operation == "setprop")
				{
    
    
					string player = arr[2].ToLower();
					string propType = arr[3].ToLower();
					int number = Convert.ToInt32(arr[4]);
					bool locke = false;
					if (number < 0 | number > 99)
					{
    
    
						Console.WriteLine("\x1b[1;31mError\x1b[0m: Number out of range (0-99)");
						Console.Write("Continue > ");
						Console.ReadLine();
						return;
					}
					if (player == "player_a" | player == "a")
					{
    
    
						switch (propType)
						{
    
    
							case "landmine": Player_A.PropSet(Player.Prop.Landmine, val: number); break;
							case "obstacle": Player_A.PropSet(Player.Prop.Obstacle, val: number); break;
							case "luckydice": Player_A.PropSet(Player.Prop.LuckyDice, val: number); break;
							case "whlocker": Player_A.PropSet(Player.Prop.WormholeLocker, val: number); break;
							default:
								locke = true;
								Console.WriteLine("\x1b[1;31mError\x1b[0m: Prop type not fouund");
								Console.Write("Continue > ");
								Console.ReadLine();
								break;
						}
					}
					else if (player == "player_b" | player == "b")
					{
    
    
						switch (propType)
						{
    
    
							case "landmine": Player_B.PropSet(Player.Prop.Landmine, val: number); break;
							case "obstacle": Player_B.PropSet(Player.Prop.Obstacle, val: number); break;
							case "luckydice": Player_B.PropSet(Player.Prop.LuckyDice, val: number); break;
							case "whlocker": Player_B.PropSet(Player.Prop.WormholeLocker, val: number); break;
							default:
								locke = true;
								Console.WriteLine("\x1b[1;31mError\x1b[0m: Prop type not fouund");
								Console.Write("Continue > ");
								Console.ReadLine();
								break;
						}
					}
					else
					{
    
    
						Console.WriteLine("\x1b[1;31mError\x1b[0m: Only have player_a and player_b");
						Console.Write("Continue > ");
						Console.ReadLine();
						return;
					}
					if (!locke) succ = true;
					Flush();
				}
				if (succ)
				{
    
    
					Console.WriteLine("\x1b[1;32mSuccess\x1b[0m");
					Thread.Sleep(500);
				}
				else
				{
    
    
					Console.WriteLine("\x1b[1;31mError\x1b[0m: Invalid debug statement");
					Thread.Sleep(1000);
				}
			}
			catch (Exception)
			{
    
    
				Console.WriteLine("\x1b[1;31mError\x1b[0m: Wrong input format");
				Console.Write("Continue > ");
				Console.ReadLine();
			}
		}

		#endregion
	}
}


Map.cs

namespace FlyChess.Resources
{
    
    
	internal class Map
	{
    
    
		#region ANSI 转义字符
		//# 颜色设置: \x1b[显色方式;前景色;背景色m ... \x1b[0m
		//# 输出内容前加\x1b[x;y;zm设置输出颜色,输出内容后加\x1b[0m清除颜色设置,否则下次输出还是上次设置的颜色
		//# 显色方式:
		//# 0:默认值
		//# 1:高亮
		//# 4:使用下划线
		//# 5:闪烁
		//# 7:反显
		//# 8:不可见

		//# 前景色  背景色  颜色说明
		//# 30      40     黑色
		//# 31      41     红色
		//# 32      42     绿色
		//# 33      43     黄色
		//# 34      44     蓝色
		//# 35      45     紫红色
		//# 36      46     青蓝色
		//# 37      47     白色

		//# 控制台光标移动指令:
		//# \x1b[1A       光标向上移动1行
		//# \x1b[1B       光标向下移动1行
		//# \x1b[1C       光标向右移动1列
		//# \x1b[1D       光标向左移动1列
		//# \x1b[1;1H     光标移动至第1行第1列

		//# 清屏指令:
		//# \x1b[0J       清空光标以下区域屏幕
		//# \x1b[1J       清空光标以上区域屏幕
		//# \x1b[2J       清空全部

		//# \x1b[0K       清空该行光标之后内容
		//# \x1b[1K       清空该行光标之前内容
		//# \x1b[2K       清空该行
		#endregion

		#region 数据成员声明与定义

		/// <summary>
		/// 格子类型:
		///	<para></para>
		///	Empty:0			空白
		///	Player_A:1		玩家 A
		///	Player_B:2		玩家 B
		///	Player_Both:3	玩家 A 和 B
		///	<para></para>
		///	Landmine:4		地雷 [后退六格, 触发后消失]
		///	<para></para>
		///	Stop:5			障碍 [暂停一回合]
		///	<para></para>
		///	Reroll:6		重掷 [重新掷一次骰子]
		///	<para></para>
		///	Wormhole:7		虫洞 [在全地图的虫洞间随机穿梭]
		///	<para></para>
		///	Prop:8			道具使用 [在此位置可使用玩家道具]
		///	<para></para>
		///	PropAdd:9		道具补给 [随机补给可获取道具之一]
		///	<para></para>
		///	Random:20 		随机 []
		///	<para></para>
		///	Win:30 			终点
		/// </summary>
		public enum GridType
		{
    
    
			Empty, Player_A, Player_B, Player_Both, Landmine, Stop, Reroll, Wormhole, Prop, PropAdd, Random = 20, Win = 30, Null = 100
		}

		/// <summary>
		/// 地图大小: Normal = 100, Medium = 140, Large = 180
		/// </summary>
		public enum MapSizeLevel
		{
    
    
			Normal = 100, Medium = 140, Large = 180
		}

		public Dictionary<int, int[]> Mode = new Dictionary<int, int[]>()
		{
    
    
			{
    
    1, new int[] {
    
     10, 5, 5, 10, 10, 3}},
			{
    
    2, new int[] {
    
     14, 7, 7, 14, 14, 4}},
			{
    
    3, new int[] {
    
     18, 9, 9, 18, 18, 5}},
		};

		/// <summary>
		/// 单元数量结构体, 存储每种特殊单元的数量, 提供初始化方法
		/// </summary>
		private struct GridCount
		{
    
    
			public int Landmine {
    
     get; set; }
			public int Stop {
    
     get; set; }
			public int Reroll {
    
     get; set; }
			public int Wormhole {
    
     get; set; }
			public int Prop {
    
     get; set; }
			public int Random {
    
     get; set; }

			/// <summary>
			/// 初始化
			/// </summary>
			/// <param name="level">地图大小等级</param>
			public void Init(int[] number)
			{
    
    
				Landmine = number[0];
				Stop = number[1];
				Reroll = number[2];
				Prop = number[3];
				Random = number[4];
				Wormhole = number[5];
			}
		}

		private Dictionary<int, string> Grid = new Dictionary<int, string>()
		{
    
    
			{
    
     0, "[  ]" },
			{
    
     1, "[\x1b[5;36mA\x1b[0m]" },
			{
    
     2, "[\x1b[5;36mB\x1b[0m]" },
			{
    
     3, "[\x1b[5;36mAB\x1b[0m]" },
			{
    
     4, "[\x1b[1;31mx\x1b[0m]" },
			{
    
     5, "[\x1b[1;33m#\x1b[0m]" },
			{
    
     6, "[\x1b[1;36mR\x1b[0m]" },
			{
    
     7, "[\x1b[1;34m卍\x1b[0m]" },
			{
    
     8, "[\x1b[1;32mv\x1b[0m]" },
			{
    
     20, "[\x1b[1;33m?\x1b[0m]" },
			{
    
     30, "[\x1b[1;31mEd\x1b[0m]" },

			{
    
     100, "	[  ]: 空白\n" },
			{
    
     101, "	[\x1b[1;36mA\x1b[0m]: 玩家A\n" },
			{
    
     102, "	[\x1b[1;36mB\x1b[0m]: 玩家B\n" },
			{
    
     103, "	[\x1b[1;36mAB\x1b[0m]: 玩家A和B\n" },
			{
    
     104, "	[\x1b[1;31mx\x1b[0m]: 地雷\n" },
			{
    
     105, "	[\x1b[1;33m#\x1b[0m]: 障碍\n" },
			{
    
     106, "	[\x1b[1;36mR\x1b[0m]: 重掷\n" },
			{
    
     107, "	[\x1b[1;34m卍\x1b[0m]: 虫洞\n" },
			{
    
     108, "	[\x1b[1;32mv\x1b[0m]: 道具使用\n" },
			{
    
     109, "	[\x1b[1;33m?\x1b[0m]: 随机效果\n" },
			{
    
     110, "	[\x1b[1;31mEd\x1b[0m]: 终点\n" },

			{
    
     200, "  [  ]: 空白   [\x1b[1;36mAB\x1b[0m]: 玩家A和B\n" },
			{
    
     201, "  [\x1b[1;36mA\x1b[0m]: 玩家A  [\x1b[1;36mB\x1b[0m]: 玩家B\n" },
			{
    
     202, "  [\x1b[1;31mx\x1b[0m]: 地雷   [\x1b[1;33m#\x1b[0m]: 障碍\n" },
			{
    
     203, "  [\x1b[1;36mR\x1b[0m]: 重掷   [\x1b[1;34m卍\x1b[0m]: 虫洞\n" },
			{
    
     204, "  [\x1b[1;32mv\x1b[0m]: 道具使用\n" },
			{
    
     205, "  [\x1b[1;33m?\x1b[0m]: 随机效果\n" },
			{
    
     206, "  [\x1b[1;31mEd\x1b[0m]: 终点\n" },
		};

		private string map_start = "+------------------------------------------------------------------------------------------------+\n|  Start ";
		private string map_feedR_1 = " -->    |";
		private string map_feedR_2 = "|                                                                                           ↓    |";
		private string map_feedR_3 = "|    <-- ";
		private string map_feedL_1 = " <--    |";
		private string map_feedL_2 = "|    ↓                                                                                           |";
		private string map_feedL_3 = "|    --> ";
		private string map_end = " End    |\n+------------------------------------------------------------------------------------------------+\n";

		private const int ROWLENGTH = 20;

		private int MapSize {
    
     get; set; }
		private int[] MapShowArray {
    
     get; set; }
		private int[] MapDataArray {
    
     get; set; }
		private int RowCount {
    
     get; set; }

		public Dictionary<int, int> WormHoleIndex {
    
     get; set; }
		private GridCount gridCount;

		#endregion

		#region 对外接口

		/// <summary>
		/// 地图单元初始化
		/// <para>mode: 游戏模式</para>
		/// </summary>
		/// <param name="mode">游戏模式</param>
		public void InitMap(Operate.GameMode mode)
		{
    
    
			switch ((int)mode)
			{
    
    
				case 1: MapSize = (int)Map.MapSizeLevel.Normal; break;
				case 2: MapSize = (int)Map.MapSizeLevel.Medium; break;
				case 3: MapSize = (int)Map.MapSizeLevel.Large; break;
			}
			MapShowArray = new int[MapSize];
			MapDataArray = new int[MapSize];
			RowCount = MapSize / ROWLENGTH;
			List<int> usedIndex = new List<int>();

			gridCount = new GridCount();
			gridCount.Init(Mode[(int)mode]);

			WormHoleIndex = new Dictionary<int, int>();

			BuildIndex(gridCount.Wormhole, GridType.Wormhole, 15, 33, ref usedIndex);
			BuildIndex(gridCount.Landmine, GridType.Landmine, 1, 98, ref usedIndex);
			BuildIndex(gridCount.Prop, GridType.Prop, 7, 98, ref usedIndex);
			BuildIndex(gridCount.Random, GridType.Random, 3, 98, ref usedIndex);
			BuildIndex(gridCount.Reroll, GridType.Reroll, 3, 98, ref usedIndex);
			BuildIndex(gridCount.Stop, GridType.Stop, 3, 98, ref usedIndex);

			int wormholeKey = 1;
			for (int i = 0; i < MapSize; i++)
			{
    
    
				MapDataArray[i] = MapShowArray[i];
				if (MapDataArray[i] == (int)GridType.Wormhole)
				{
    
    
					WormHoleIndex.Add(wormholeKey, i);
					wormholeKey++;
				}
			}

			usedIndex.Add(0);
			MapShowArray[0] = 3;
			usedIndex.Add(MapSize - 1);
			MapShowArray[MapSize - 1] = 30;
			MapDataArray[MapSize - 1] = 30;
		}

		/// <summary>
		/// 输出地图
		/// </summary>
		public void PrintMap()
		{
    
    
			string print = map_start;
			int mapIndex = 0;
			int mapIndex_R = 0;
			int legendIndex = 200;
			if (MapSize > 100) legendIndex = 100;
			for (int i = 0; i < RowCount; i++)
			{
    
    
				if (i % 2 == 0)
				{
    
    
					for (int j = 0; j < ROWLENGTH; j++)
					{
    
    
						print += Grid[MapShowArray[mapIndex]];
						mapIndex++;
					}
				}
				else
				{
    
    
					mapIndex += 20;
					mapIndex_R = mapIndex - 1;
					for (int j = 0; j < ROWLENGTH; j++)
					{
    
    
						print += Grid[MapShowArray[mapIndex_R]];
						mapIndex_R--;
					}
				}
				if (i != RowCount - 1)
				{
    
    
					if (i % 2 == 0)
					{
    
    
						print += map_feedR_1;
						if (Grid.ContainsKey(legendIndex)) {
    
     print += Grid[legendIndex]; legendIndex++; }
						else {
    
     print += "\n"; }
						print += map_feedR_2;
						if (Grid.ContainsKey(legendIndex)) {
    
     print += Grid[legendIndex]; legendIndex++; }
						else {
    
     print += "\n"; }
						print += map_feedR_3;
					}
					else
					{
    
    
						print += map_feedL_1;
						if (Grid.ContainsKey(legendIndex)) {
    
     print += Grid[legendIndex]; legendIndex++; }
						else {
    
     print += "\n"; }
						print += map_feedL_2;
						if (Grid.ContainsKey(legendIndex)) {
    
     print += Grid[legendIndex]; legendIndex++; }
						else {
    
     print += "\n"; }
						print += map_feedL_3;
					}
				}
			}
			print += map_end;
			Console.Write(print);
		}

		/// <summary>
		/// 设置地图
		/// <para>index:坐标, type:类型</para>
		/// </summary>
		/// <param name="index">坐标</param>
		/// <param name="type">类型</param>
		public void SetMapShowArray(int index, GridType type = GridType.Null, int value = 100)
		{
    
    
			if (type != GridType.Null) MapShowArray[index] = (int)type;
			else if (value != 100) MapShowArray[index] = value;
		}
		public void SetMapDataArray(int index, GridType type = GridType.Null, int value = 100)
		{
    
    
			if (type != GridType.Null) MapDataArray[index] = (int)type;
			else if (value != 100) MapDataArray[index] = value;
		}

		/// <summary>
		/// 获取地图坐标值
		/// </summary>
		/// <param name="index">坐标</param>
		/// <returns></returns>
		public int GetMapShowArray(int index)
		{
    
    
			return MapShowArray[index];
		}
		public int GetMapDataArray(int index)
		{
    
    
			return MapDataArray[index];
		}

		/// <summary>
		/// 获取地图数据
		/// </summary>
		/// <returns></returns>
		public int[] GetMapData()
		{
    
    
			return MapDataArray;
		}

		/// <summary>
		/// 获取虫洞数量
		/// </summary>
		/// <returns>返回虫洞的数量</returns>
		public int GetWormholeCount()
		{
    
    
			return gridCount.Wormhole;
		}

		/// <summary>
		/// 设置虫洞数量
		/// </summary>
		public void SetWormholeCount(int count)
		{
    
    
			gridCount.Wormhole = count;
		}

		/// <summary>
		/// 获取地图大小
		/// </summary>
		/// <returns>返回地图大小</returns>
		public int GetMapSize()
		{
    
    
			return MapSize;
		}

		/// <summary>
		/// 更新单元数量统计
		/// </summary>
		public void UpdateGridCount()
		{
    
    
			gridCount.Landmine = 0;
			gridCount.Reroll = 0;
			gridCount.Prop = 0;
			gridCount.Random = 0;
			gridCount.Stop = 0;
			gridCount.Wormhole = 0;

			foreach (int val in MapDataArray)
			{
    
    
				switch (val)
				{
    
    
					case (int)GridType.Landmine:
						gridCount.Landmine++;
						break;
					case (int)GridType.Prop:
						gridCount.Prop++;
						break;
					case (int)GridType.Random:
						gridCount.Random++;
						break;
					case (int)GridType.Reroll:
						gridCount.Reroll++;
						break;
					case (int)GridType.Stop:
						gridCount.Stop++;
						break;
					case (int)GridType.Wormhole:
						gridCount.Wormhole++;
						break;
				}
			}
		}

		/// <summary>
		/// 更新虫洞编号
		/// </summary>
		public void UpdateWormholeIndex()
		{
    
    
			WormHoleIndex.Clear();
			int number = 1;
			for (int i = 0; i < MapSize; i++)
			{
    
    
				if (MapDataArray[i] == (int)GridType.Wormhole)
				{
    
    
					WormHoleIndex.Add(number, i);
					number++;
				}
				gridCount.Wormhole = number - 1;
			}
		}

		#endregion

		#region 中间方法

		/// <summary>
		/// 自动构建类型单元, 服务于 InitMap 方法
		/// </summary>
		/// <param name="gridCount">单元数量</param>
		/// <param name="gridType">单元类型</param>
		/// <param name="distance_L">最小间距</param>
		/// <param name="distance_H">最大间距</param>
		/// <param name="usedIndex">已用索引列表</param>
		private void BuildIndex(int gridCount, GridType gridType, int distance_L, int distance_H, ref List<int> usedIndex)
		{
    
    
			Random random = new Random();
			List<int> indexs = new List<int>();
			for (int i = 0; i < gridCount; i++)
			{
    
    
				while (true)
				{
    
    
					int index = random.Next(1, MapSize - 1);
					if (usedIndex.Contains(index)) continue;
					else
					{
    
    
						bool pass = true;
						bool conti = true;
						foreach (int j in indexs)
						{
    
    
							if (Math.Abs(j - index) < distance_L) {
    
     pass = false; conti = false; break; }
						}
						if (conti)
						{
    
    
							int l = 0;
							int h = 0;
							foreach (int j in indexs)
							{
    
    
								if (j < index) l = j;
								if (j > index) {
    
     h = j; break; }
							}
							if (l != 0) {
    
     if (Math.Abs(l - index) > distance_H) pass = false; }
							if (h != 0) {
    
     if (Math.Abs(h - index) > distance_H) pass = false; }
							if (pass)
							{
    
    
								indexs.Add(index);
								indexs.Sort();
								usedIndex.Add(index);
								MapShowArray[index] = (int)gridType;
								break;
							}
							else continue;
						}
					}
				}
			}
		}

		#endregion


		#region 测试

		public void Test()
		{
    
    
			foreach (int type in MapShowArray)
			{
    
    
				Console.Write($"{
      
      type} ");
			}
			Console.WriteLine();
		}

		#endregion
	}
}

Player.cs

namespace FlyChess.Resources
{
    
    
	internal class Player
	{
    
    
		#region 数据成员声明与定义

		/// <summary>
		/// Landmine: 地雷, Obstacle: 障碍, LuckyDice: 幸运骰子, WormholeLocker: 虫洞锁闭器
		/// </summary>
		public enum Prop
		{
    
    
			Landmine, Obstacle, LuckyDice, WormholeLocker
		}
		public static string[] PropName {
    
     get; set; } = new string[]
		{
    
    
			"Landmine", "Obstacle", "LuckyDice", "WormholeLocker"
		};

		/// <summary>
		/// 不同游戏模式对应的道具数量
		/// </summary>
		public Dictionary<int, int[]> Mode = new Dictionary<int, int[]>()
		{
    
    
			{
    
     1, new int[4]{
    
     3, 3, 3, 0} },
			{
    
     2, new int[4]{
    
     5, 5, 4, 1} },
			{
    
     3, new int[4]{
    
     7, 7, 5, 1} },
		};

		/// <summary>
		/// 道具使用次数, 存储道具数量
		/// </summary>
		public struct PropCount
		{
    
    
			public Dictionary<Prop, int> Count {
    
     get; set; } = new Dictionary<Prop, int>()
			{
    
    
				{
    
     Prop.Landmine, 0 },
				{
    
     Prop.Obstacle, 0 },
				{
    
     Prop.LuckyDice, 0 },
				{
    
     Prop.WormholeLocker, 0 },
			};

			public PropCount(int[] number)
			{
    
    
				Count[Prop.Landmine] = number[0];
				Count[Prop.Obstacle] = number[1];
				Count[Prop.LuckyDice] = number[2];
				Count[Prop.WormholeLocker] = number[3];
			}
		}

		public int Step {
    
     get; set; } = 0;
		public Dictionary<Map.GridType, int> Experience {
    
     get; set; } = new Dictionary<Map.GridType, int>()
		{
    
    
			{
    
     Map.GridType.Landmine, 0 },
			{
    
     Map.GridType.Prop, 0 },
			{
    
     Map.GridType.PropAdd, 0 },
			{
    
     Map.GridType.Random, 0 },
			{
    
     Map.GridType.Reroll, 0 },
			{
    
     Map.GridType.Stop, 0 },
			{
    
     Map.GridType.Wormhole, 0 },
		};

		public int Location {
    
     get; set; }
		public PropCount PCount;

		#endregion

		#region 对外接口

		public Player(Operate.GameMode mode)
		{
    
    
			Location = 0;
			PCount = new PropCount(Mode[(int)mode]);
		}

		/// <summary>
		/// 输出道具数量详情
		/// </summary>
		public void PrintPropMessage()
		{
    
    
			Console.WriteLine($"Props: [地雷:\x1b[1;34m{
      
      PCount.Count[Prop.Landmine]}\x1b[0m  障碍:\x1b[1;34m{
      
      PCount.Count[Prop.Obstacle]}\x1b[0m  幸运骰子:\x1b[1;34m{
      
      PCount.Count[Prop.LuckyDice]}\x1b[0m  虫洞锁闭器:\x1b[1;34m{
      
      PCount.Count[Prop.WormholeLocker]}\x1b[0m]");
		}

		/// <summary>
		/// 道具使用, 自动检查道具余量, 若足够则余量减一, 若不足给出提示
		/// </summary>
		/// <param name="p">道具种类</param>
		/// <returns>使用成功返回 true, 否则返回 false</returns>
		public bool PropUse(Prop p)
		{
    
    
			if (PCount.Count[p] == 0)
			{
    
    
				Console.WriteLine("\x1b[1;31mError\x1b[0m: Insufficient items");
				Console.ReadLine();
				return false;
			}
			else
			{
    
    
				PCount.Count[p]--;
				return true;
			}
		}

		/// <summary>
		/// 道具补给, 指定道具使其可用次数增加
		/// </summary>
		/// <param name="p">道具种类</param>
		/// <param name="add">增加值</param>
		public void PropAdd(Prop p, int add)
		{
    
    
			PCount.Count[p] += add;
		}

	    /// <summary>
		/// 道具数量设置
		/// </summary>
		/// <param name="p">道具种类</param>
		/// <param name="val">设定值</param>
		public void PropSet(Prop p, int val)
		{
    
    
			PCount.Count[p] = val;
		}

		#endregion

		#region 中间方法



		#endregion

		#region 测试



		#endregion
	}
}

其他问题

运行时请使用 Windows PowerShell 或者 Terminal ,我自己测试在这两个上面运行效果良好

如果需要 VisualStudio 工程文件请联系我:
私信(可能回复较晚)、邮箱:[email protected] (推荐)

猜你喜欢

转载自blog.csdn.net/NEKOic/article/details/127506349