[Day 006 of C#] 飞行棋 on Console —— Chuchu~~! Score
Day 006
part 090 - 097.
飞行棋 on Console
大概思路
- 画出一个游戏头
- 初始化地图(加载地图所需要的资源)
- 把int数组里面的数字变成字符串输出的过程,就叫做初始化地图
- 画地图
- 可以拿数组存取地图,因为要访问到地图上的每一个点
- 玩游戏
游戏规则
如果玩家A踩到玩家B 玩家B退6格
如果踩到地雷 退6格
踩到时空隧道 进10格
踩到幸运轮盘: 1. 交换位置; 2.轰炸对方,使对方退6格
踩到暂停,自己暂停一回合
踩到方块,无事发生
具体代码及注释
- 具体代码与详细注释,具体的思路就都在下面体现好了:3
using System;
namespace _Day_006ofCSharp{
internal class Program{
static void Main(string[] args){
Head();
#region 玩家姓名的输入
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("请输入玩家A的姓名");
_Name[0] = Console.ReadLine();
while (_Name[0] == "") {
Console.WriteLine("姓名不能为空,请重新输入");
_Name[0] = Console.ReadLine();
}
Console.WriteLine("请输入玩家B的姓名");
_Name[1] = Console.ReadLine();
while (_Name[1] == "" || _Name[1] == _Name[0]) {
if (_Name[1] == "") {
Console.WriteLine("姓名不能为空,请重新输入");
}else {
Console.WriteLine("玩家B的姓名不能跟玩家A的相同,请重新输入");
_Name[1] = Console.ReadLine();
}
}
#endregion
//玩家姓名输入完后应该清屏,再重新调用游戏头
Console.Clear(); //清屏
Console.WriteLine("{0}的大飞机用A表示", _Name[0]);
Console.WriteLine("{0}的大飞机用B表示", _Name[1]);
Head();
MapLoading();
MapDrawing();
//要先初始化地图,要不然全地图坐标数组中元素都为0,MapDrawing函数也便无法工作了
//当玩家A、B没有一个人在终点的时候,两个玩家不停地去玩游戏。
while (_Spot[0] < 99 && _Spot[1] < 99) {
if (Flags[0] == false) GamePlay(0);
else Flags[0] = false;
if (_Spot[0] >= 99) {
Console.WriteLine("玩家{0}不曾输,玩家{1}不曾赢", _Name[0], _Name[1]);
break;
}
if (Flags[1] == false) GamePlay(1);
else Flags[1] = false;
if (_Spot[1] >= 99) {
Console.WriteLine("玩家{0}不曾输,玩家{1}不曾赢", _Name[1], _Name[2]);
break;
}
}
Win();
Console.ReadKey();
}
/// <summary>
/// 胜利
/// </summary>
public static void Win() {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(" ◆ ");
Console.WriteLine(" ■ ◆ ■ ■");
Console.WriteLine(" ■■■■ ■ ■ ◆■ ■ ■ ■");
Console.WriteLine(" ■ ■ ■ ■ ◆ ■ ■ ■ ■");
Console.WriteLine(" ■ ■ ■■■■■■ ■■■■■■■ ■ ■ ■");
Console.WriteLine(" ■■■■ ■ ■ ●■● ■ ■ ■");
Console.WriteLine(" ■ ■ ■ ● ■ ● ■ ■ ■");
Console.WriteLine(" ■ ■ ■■■■■■ ● ■ ● ■ ■ ■");
Console.WriteLine(" ■■■■ ■ ● ■ ■ ■ ■ ■");
Console.WriteLine(" ■ ■ ■ ■ ■ ■ ■ ■");
Console.WriteLine(" ■ ■ ■ ■ ■ ■ ");
Console.WriteLine(" ■ ■ ■ ■ ● ■ ");
Console.WriteLine(" ■ ■■ ■■■■■■ ■ ● ●");
Console.ResetColor();
}
/// <summary>
/// 游戏头(Head)
/// 输出游戏信息,并有了颜色。
/// 注意:由于字体不同,这里排版时需要自己先打开控制台看一看是否对齐。
/// </summary>
public static void Head(){
#region 游戏头
// Console.BackgroundColor 这是背景色
// Console.ForegroundColor 这是前景色
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("************************");
//第一行颜色Cyan
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("************************");
//第二行颜色Yellow
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("************************");
Console.WriteLine("**Bing's Homemade飞行棋**");
Console.WriteLine("************************");
//第三、四、五行颜色Red
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("************************");
//第六行颜色Yellow
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("************************");
//第七行颜色Cyan
#endregion
}
/// </summary>
public static int[] _Map = new int[100];
//声明一个静态字段_Map,模拟全局变量,好让所有的函数都能访问到存地图的数组
public static int[] _Spot = new int[2];
//声明一个静态字段_Spot,模拟全局变量,存储玩家A、B的位置,好让所有的函数都能访问到存位置Spot的数组。其中,_Spot[0]是A的位置,_SpotB是B的位置
public static string[] _Name = new string[2];
//声明一个静态字段_Name,模拟全局变量,存储玩家A、B的名字,好让所有的函数都能访问到存位置Name的数组。其中,_Name[0]是A的位置,_Name[1]是B的位置
public static bool[] Flags = new bool[2]; // Flags[0]和Flags[1]默认都是Flase
//声明一个静态字段Flags,模拟全局变量,用于暂停
/// <summary>
/// 初始化地图(_MapLoading)
/// 分别遍历已知关卡坐标的数组,并将这些数组的元素当作是全地图坐标数组的下标,然后再把对应的元素赋值为对应的表示数(幸运轮盘为1,2为地雷,3为暂停,4是时空隧道)
public static void MapLoading(){
#region 地图初始化
int[] luckyRoulette = { 6, 23, 40, 55, 69, 83 };
//已知幸运轮盘在地图中的坐标,且幸运轮盘的表示数为1
for (int i = 0; i < luckyRoulette.Length; i++) Map[luckyRoulette[i]] = 1;
//遍历存取幸运轮盘坐标的数组,并将幸运轮盘坐标数组中的每一个元素当作是全地图坐标数组元素的下标,在全地图坐标所对应的元素赋值为1.(例如,当遍历幸运轮盘坐标数组至23时,就把全地图坐标数组中下标为23的元素赋值为1)
int[] landmine = { 5, 13, 17, 33, 38, 50, 64, 80, 94 };
//地雷在地图中的坐标
for (int i = 0; i < landmine.Length; i++) Map[landmine[i]] = 2;
//遍历存取地雷坐标的数组,并将地雷坐标数组中的每一个元素当作是全地图坐标数组元素的下标,在全地图坐标所对应的元素赋值为2.(例如,当遍历地雷坐标数组至33时,就把全地图坐标数组中下标为33的元素赋值为2)
int[] pause = { 9, 27, 60, 93 };
//暂停在地图中的坐标
for (int i = 0; i < pause.Length; i++) Map[pause[i]] = 3;
//遍历存取暂停坐标的数组,并将暂停坐标数组中的每一个元素当作是全地图坐标数组元素的下标,在全地图坐标所对应的元素赋值为3.(例如,当遍历暂停坐标数组至93时,就把全地图坐标数组中下标为93的元素赋值为3)
int[] timeMachine = { 20, 25, 45, 63, 72, 88, 90 };
//时空隧道在地图中的坐标
for (int i = 0; i < timeMachine.Length; i++) Map[timeMachine[i]] = 4;
//遍历存取时空隧道坐标的数组,并将时空隧道坐标数组中的每一个元素当作是全地图坐标数组元素的下标,在全地图坐标所对应的元素赋值为4.(例如,当遍历时空隧道坐标数组至93时,就把全地图坐标数组中下标为63的元素赋值为4)
#endregion
}
/// <summary>
/// 地图的绘制
/// </summary>
public static void MapDrawing(){
#region 输出第一横行
Console.WriteLine("图例: 幸运轮盘◎ \t 地雷❤ \t 暂停▲ \t 时空隧道☯");
for (int i = 0; i < 30; i++) Console.Write(LinesofMapDraing(i)); //调用方法对第一横行的关卡进行输出
#endregion
Console.WriteLine(); //画完一行后应该换行
#region 第一竖行
//画一坨空格,再画一个关卡,即外面做N件事,里面做1件事,便联想到For循环的嵌套
for (int i = 30; i < 35; i++){ //画关卡
for (int j = 0; j < 29; j++) Console.Write(" "); //前29格都是空格
Console.Write(LinesofMapDraing(i));//调用方法对第一竖行的关卡进行输出
Console.WriteLine(); //画完一竖行后应该换行
}
#region 第二横行
for (int i = 64; i >= 35; i+--) Console.Write(LinesofMapDraing(i));
//调用方法对第二横行的关卡进行输出
//因为第二横行是从右到左走的,所以要倒序输出,从第二横行的结尾64开始走
//当然也可以调用LinesofMapDraing函数后,把返回值依次添加到一个字符串中,再进行Reverse处理,不过太麻烦了...?
Console.WriteLine(); //画完一行后应该换行
#endregion
#region 第二竖行
//第二竖行不需要用空格进行排版,所以无需for循环嵌套
for (int i = 65; i < 70; i++) Console.WriteLine(LinesofMapDraing(i));//调用方法对第一竖行的关卡进行输出
#endregion
#region 输出第三横行
for (int i = 70; i < 100; i++) Console.Write(LinesofMapDraing(i)); //调用方法对第三横行的关卡进行输出
#endregion
Console.WriteLine(); //画完最后一行,应该换行
} //函数MapDrawing
/// <summary>
/// 从画地图的函数中抽象出来的一个函数
/// 画行
/// </summary>
/// <param name="i">一行内的关卡</param>
/// <returns>关卡的输出</returns>
public static string LinesofMapDraing(int i){
#region 地图行的封装
//因为是行,所以无需换行
string str = "";
//因为地图是死的,玩家的位置是活的,所以要先输出A和B,因此还要再在后面写上,不在A、B的位置上输出关卡的语句
if(_Spot[0] == _Spot[1] && _Spot[0] == i) {
Console.ForegroundColor = ConsoleColor.White; //当坐标相同时的"<>"显示为白色
str = "<>"; //如果玩家A、B坐标相同,且都在地图上,输出"<>"
}
else if(_Spot[0] == i) {
Console.ForegroundColor = ConsoleColor.Red; //玩家A显示红色
str = "A";
}
//判断玩家A的位置在数轴上的位置,并在相应的位置输出一个"A"
else if (_Spot[1] == i) {
Console.ForegroundColor = ConsoleColor.Blue; //玩家B显示蓝色
str = "B";
}
//判断玩家B的位置在数轴上的位置,并在相应的位置输出一个"B"
else { //如果遍历到的位置都不是A、B的位置,那就输出关卡
switch(_Map[i]) {//运用switch-case,判断条件是第一横行的坐标值
case 0:
Console.ForegroundColor = ConsoleColor.Gray; //方格显示灰色
str = "□"; //普通方格
break;
case 1:
Console.ForegroundColor = ConsoleColor.Magenta; //幸运轮盘显示洋红色
str = "◎"; //幸运轮盘
break;
case 2: str = "❤"; //地雷
Console.ForegroundColor = ConsoleColor.Red; //地雷显示红色
break;
case 3:
Console.ForegroundColor = ConsoleColor.Green; //暂停显示黄色
str = "▲"; //暂停
break;
case 4:
Console.ForegroundColor = ConsoleColor.Yellow; //时空隧道显示绿色
str = "☯"; //时空隧道
break;
}// switch关卡位置输出
}// else关卡位置输出
return str;
#endregion
}
public static void GamePlay(int player) {
Random r = new Random(); // 产生随机数
int rNumber = r.Next(1, 7); //限定了 Next产生的随机数在1-7(不含7)中,返回给一个int类型的rNumber
Console.WriteLine("{0}按任意键开始掷骰子", _Name[player]);
Console.ReadKey(true);
Console.WriteLine("{0}掷出了{1}", _Name[player], rNumber);
_Spot[player] += rNumber;
SpotChanging();
Console.ReadKey(true);
Console.WriteLine("{0}按任意键开始行动", _Name[player]);
Console.ReadKey(true);
Console.WriteLine("{0}行动完了", _Name[player]);
Console.ReadKey(true);
//玩家A有可能踩到了玩家B 方块 幸运轮盘 地雷 暂停 时空隧道
if (_Spot[player] == _Spot[1 - player]) {
// 因为player要么等于0,要么等予1,所以用1-player来表示另外一个玩家
Console.WriteLine("玩家{0}踩到了玩家{1},玩家{2}退6格", _Name[player], _Name[1 - player], _Name[1 - player]);
_Spot[1 - player] -= 6;
SpotChanging();
Console.ReadKey(true);
}
else {//踩到了关卡
//玩家的坐标
switch (_Map[_Spot[player]]) {
case 0:
Console.WriteLine("玩家{0}踩到了方块,安全。", _Name[player]);
Console.ReadKey(true);
break;
case 1:
Console.WriteLine("玩家{0}踩到了幸运轮盘,请选择 1--交换位置 2--轰炸对方", _Name[player]);
string input = Console.ReadLine();
while (true) {
if (input == "1") {
Console.WriteLine("玩家{0}选择跟玩家{1}交换位置", _Name[player], _Name[1 - player]);
Console.ReadKey(true);
int temp = _Spot[player];
_Spot[player] = _Spot[1 - player];
_Spot[1 - player] = temp;
Console.WriteLine("交换完成!!!按任意键继续游戏!!!");
Console.ReadKey(true);
break;
}
else if (input == "2") {
Console.WriteLine("玩家{0}选择轰炸玩家{1},玩家{2}退6格", _Name[player], _Name[1 - player], _Name[1 - player]);
Console.ReadKey(true);
_Spot[1 - player] -= 6;
Console.WriteLine("玩家{0}退了6格", _Name[1 - player]);
Console.ReadKey(true);
break;
}
else {
Console.WriteLine("只能输入1或者2 1--交换位置 2--轰炸对方");
input = Console.ReadLine();
}
}
break;
case 2:
Console.WriteLine("玩家{0}踩到了地雷,退6格", _Name[player]);
Console.ReadKey(true);
_Spot[player] -= 6;
break;
case 3:
Console.WriteLine("玩家{0}踩到了暂停,暂停一回合", _Name[player]);
Flags[player] = true;
Console.ReadKey(true);
break;
case 4:
Console.WriteLine("玩家{0}踩到了时空隧道,前进10格", _Name[player]);
_Spot[player] += 10;
Console.ReadKey(true);
break;
} //switch
} //else
SpotChanging(); //在清屏前再调一次SpotChanging,避免每一次改变位置都要去调用SpotChanging函数。
Console.Clear();
MapDrawing(); //重新绘制
}
/// <summary>
/// 当玩家坐标发生改变时调用此函数
/// </summary>
public static void SpotChanging() {
if (_Spot[0] < 0) _Spot[0] = 0;
if (_Spot[0] >= 99) _Spot[0] = 99;
if (_Spot[1] < 0) _Spot[1] = 0;
if (_Spot[1] >= 99) _Spot[1] = 99;
}
}
}
新知识
控制台文本的前景色与背景色
Console.BackgroundColor //这是背景色
Console.ForegroundColor //这是前景色
Console.ResetColor() //这用于重置颜色
- 在上面,BackgroundColor和ForegroundColor都是Console这个类的属性
- 那我们怎么才能知道怎么给属性赋值呢?
- 可以将光标移到属性上,看左上角的类型。在这里,这两个属性所需要的值都是
Console.Color
类型的,是一个枚举(有两个黄色方块)。所以我们就可以:
Console.ForegroundColor = ConsoleColor.DarkMagenta;
// DarkMagenta是一种颜色,在这里还可以使用red/ yellow/ 等
ReadKey()函数
- ReadKey()也是一个函数,所以它可能就会有参数
- 将光标移到ReadKey()上,就能发现它有用到函数的重载
- 我们便发现他的形参是 bool intercept,并且知道了,如果intercept确定是否在控制台窗口中显示按下的键。如果为True,则不现实按下的键;否则为False
- 因此,我们如果不想让控制台窗口中显示按下的键,可以这么写
Console.ReadKey(true);
体会
- 在对于写函数没有如何架设整体框架的头绪时,我们可以先在Main函数里面,单方面地、针对性地去写一个功能,再把它改装成一个函数,把死的改成活的
- 在像飞行棋这种程序中,利用int整数类型数组去代替、描述一种位置,非常方便,因为这样子能很容易的进行遍历、访问、替换
sentences
学了一天的方法看不懂数学书上的一次函数呜呜呜呜我是憨憨