目录
- C# 控制台飞行棋小游戏
- 简要介绍
- 游戏画面
- 规则说明
- 游戏代码
- `Entry.cs`
- `Operate.cs`
- `Map.cs`
- `Player.cs`
- 其他问题
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_a
、player_b
的形式,也可以直接写a
、b
,大小写任意<gridType>
可选类型为empty
、landmine
、stop
、reroll
、random
、prop
、wormhole
分别对应 空白、地雷、障碍、重掷、随机效果、道具使用、虫洞<propType>
可选类型为landmine
、obstacle
、luckydice
、whlocker
分别对应 地雷、障碍、幸运骰子、虫洞闭锁器<value>
为设置的数量(0-99之间任意值)
游戏代码
游戏的代码并不是很复杂,仅有四个 .cs
文件,分别为 Entry.cs
和 Operate.cs
,以及两个实体类Map.cs
、Player.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 ,我自己测试在这两个上面运行效果良好