C# 控制台推箱子游戏
lib:
lib:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace pushbox
{
public enum valueType { X, Y }//坐标类型枚举
public enum ObjType { people = 0, box = 1, food = 2, }//对象类型枚举
public class obj//强大的对象
{
Random Rnd = new Random();//实例化一个随机数生成对象
/// <summary>
/// 初始化一个对象
/// </summary>
/// <param name="_type">设置对象类型</param>
/// <param name="_add">给一个添加到List的方法</param>
/// <param name="_print">给一个打印方法</param>
/// <param name="_findobj">给一个查找方法</param>
/// <param name="_x_max">场景宽(带默认)</param>
/// <param name="_y_max">场景高(带默认)</param>
public obj(ObjType _type, Action<obj> _add,Action _print,Func<string,ObjType,List<obj>> _findobj, int _x_max = 22, int _y_max = 11)
{
_objtype = _type;//设置类型
x_max = _x_max;//场景大小存起来,以后要用
y_max = _y_max;
NewPoint();//随机产生坐标
_add(this);//添加的方法存起来
Print = _print;//打印的方法存起来
FindObj = _findobj;//查找的方法存起来
}
/*--定义属性下的字段*/
int _x;
int _y;
private int x_max;
private int y_max;
public string newStrType = string.Empty;//自定义显示的字符
ObjType _objtype;
string[] showObj = new string[3] { "人", "箱", "星" }; //显示内容的集合
private obj Next { get; set; }//将要推动的对象
private obj Last { get; set; }//推动本人的对象
public ObjType type { get { return _objtype; } }//返回对象类型
public override string ToString() { return "(" + x.ToString() + "," + y.ToString() + ")"; }//重写转换字符串操作,返回字符串类型坐标
public void NewPoint() { _x = 1 + Rnd.Next(x_max - 2); _y = 1 + Rnd.Next(y_max - 2); }//生成随机坐标方法
public Action Print { get; set; }//打印的方法
private Func<string,ObjType,List<obj>> FindObj { get; set; }//查找的方法,返回一个对象
private int NextMove { get; set; }//移动方向
/// <summary>
/// 返回显示的字符串,可以自定义
/// </summary>
public string strType
{
get
{
if (newStrType == string.Empty)
return showObj[_objtype.GetHashCode()];
else
return newStrType;
}
set
{
newStrType = value;
}
}
public int x//坐标
{
get { return _x; }
set
{
NextMove = value - _x;
if (OnValueChange(value, valueType.X, type))
{
_x = value;
DoFind(valueType.X);
Print();
}
else
Back(valueType.X);
}
}
public int y//坐标
{
get { return _y; }
set
{
NextMove = value - _y;//存下操作的方向
if (OnValueChange(value, valueType.Y, type))//能否允许改变
{
_y = value;
DoFind(valueType.Y);//改变后寻找身边的对象
Print();//打印
}
else
Back(valueType.Y);//移动失败,退回原来状态
}
}
/// <summary>
/// 移动失败,回滚
/// </summary>
/// <param name="valuetype">坐标类型</param>
private void Back(valueType valuetype)
{
if (Last == null)
return;
if (valuetype == valueType.X)
Last._x = Last._x - NextMove;
else
Last._y = Last._y - NextMove;
}
/// <summary>
/// 值改变时间
/// </summary>
/// <param name="value">改变的新值</param>
/// <param name="valueType">坐标类型</param>
/// <param name="objType">对象类型</param>
/// <returns></returns>
private bool OnValueChange(int value, valueType valueType, ObjType objType)
{
if (objType == ObjType.people)//是不是人动了
return CheckValue(value, valueType, 0);//人可以走到最边上
else
return CheckValue(value, valueType, -1);//但箱子不行
}
private bool CheckValue(int value, valueType valueType, int addmax)
{
if (valueType == valueType.X)
return 0 + Math.Abs(addmax) <= value && value < (x_max + addmax) ? true : false;//x值能否合法
else
return 0 + Math.Abs(addmax) <= value && value < (y_max + addmax) ? true : false;//y值能否合法
}
/// <summary>
/// 查找事件
/// </summary>
/// <param name="valuetype"></param>
private void DoFind(valueType valuetype)
{
if (FindObj(this.ToString(),ObjType.box).Count == 0)//找不到,算了
return;
Next = FindObj(this.ToString(), ObjType.box)[0];//找到了存下来
if (type == ObjType.people)//本人是人
{
if (valuetype == valueType.X)//去推箱子
{
Next.Last = this;
Next.x = Next.x + NextMove;
}
else
{
Next.Last = this;
Next.y = Next.y + NextMove;
}
}
else if (type == ObjType.box)//本人是箱子
{
if (FindObj(this.ToString(), ObjType.box).Count > 1)//看有没有与箱子重叠
{
if (valuetype == valueType.X)//重叠了回滚
{
this._x = this._x - NextMove;
Last._x = Last._x - NextMove;
}
else
{
this._y = this._y - NextMove;
Last._y = Last._y - NextMove;
}
}
else if (FindObj(this.ToString(), ObjType.food).Count > 0)//能否与food重叠,改变显示的字符
this.strType = "興";
else
this.strType = "箱";//都不是,还原回原来的字符
}
}
}
}
Program:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace pushbox
{
class Program
{
static int x_max = 22;//场景大小
static int y_max = 11;
public static List<obj> ObjList = new List<obj>();//建立一个集合存全部的对象
private static void print()//打印时间,实例化是传给对象
{
string show = string.Empty;//初始化
for (int j = 0; j < y_max; j++)//循环去写每一个坐标
{
for (int i = 0; i < x_max; i++)
{
string strPoint = "(" + i.ToString() + "," + j.ToString() + ")";//转换成字符串型的坐标
obj obj = ObjList.Find(o => o.ToString() == strPoint);//遍历所以对象有没有一样的
if (obj != null)
show += obj.strType;//有的话就显示出来
else
show += "空";//没有就补格子
}
show += "\r\n";//换行
}
Console.Write("\r\n" + show + "\r\n");//输出
}
private static List<obj> FindObj(string point,ObjType objtype)//查找时间,实例化时传入对象
{
return ObjList.FindAll(o=>o.type == objtype && o.ToString() == point);
}
static void Main(string[] args)
{
obj people = new obj(ObjType.people, p => ObjList.Add(p),print,FindObj);//实例化人
System.Threading.Thread.Sleep(50);//延时,不延时随机数忙不过来,都产生一样的随机数
for (int i = 0; i < 10; i++)//生成一堆箱子
{
obj box = new obj(ObjType.box, b => ObjList.Add(b), print, FindObj);
System.Threading.Thread.Sleep(50);
}
for (int i = 0; i < 10; i++)//生成一堆星星
{
obj food = new obj(ObjType.food, f => ObjList.Add(f), print, FindObj);
System.Threading.Thread.Sleep(50);
}
print();
ConsoleKeyInfo Key;//存按键
do
{
Key = Console.ReadKey();//按键捕捉
switch (Key.Key)
{
case ConsoleKey.A:
people.x = people.x - 1;
break;
case ConsoleKey.D:
people.x = people.x + 1;
break;
case ConsoleKey.S:
people.y = people.y + 1;
break;
case ConsoleKey.W:
people.y = people.y - 1;
break;
}
} while (true);//不断循环
}
}
}
人代表人物,箱代表箱子,星代表星星,興代表装着星星的箱子,空代表没有东西,本来用符号的,但特殊字符发不上来改成文字。
这是一个推箱子游戏的代码,不难看出,本人非常喜欢在属性的set里面带上一个方法去作为一个值变更事件来用,并为这种方法命名为属性流,但有人说这种方法不可取,在这讨教下各位高手们,这种方法有什么利与弊,究竟利大还是弊大?
解决方案
10
死循环是你本人写的有问题
至少Control.Text里就调用OnTextChanged事件,说明这样做是合理的
至少Control.Text里就调用OnTextChanged事件,说明这样做是合理的
20
说“可取”或“不可取”这种话有点空洞了,你应该理解你所谓的“有人说”时的背后的理由,而不是纠结一些带有情绪化的肤浅的词儿。一旦你去真正去理解理由,那么不同理由就会产生不同的结果,绝不能用本人的洁癖去要求别人,不能用本人所见去要求别人的是非,这时候你就不会动不动就用绝对化的词儿来说事非,而是比较客观地去讨论不同人的设计想法的差异性。
假设有一个位置是(2,1),另一个位置是(3,2),假设这是“积木系统”可以从位置1将积木移动到位置2,那么你所谓的“先设置x为3、后设置y为2”跟“先设置y为2、后设置x为3”有区别吗?
对于静态概念,那么“坐标位置”概念本身是x-y方式标识的,是不可分割的两部分,不区分谁先谁后。对于动态行为概念,那么才会去用各种真正对应于实际领域的方式去设计它。因此一般来说,对于动态行为,会使用”goto(2,1)”一个数据作为第一个操作数据,然后使用“goto(3,2)”作为第二个操作数据。
例如说,一个人从上海到达了北京,问一下这个事件跟“这个人的左脚先从上海到达了北京,然后他的右脚又从上海到达了北京”这样的描述有区别吗?假如你觉得没有区别,那么你可以继续去研究。但是别人假设预见到了你这种不靠谱、不真实的冗余表述方式“不可取”,你应该理解他以什么事实为本,而你以什么来对付(凑合)。你从他的角度去理解,而不是纠结什么事非。
任何东西的是非争议都是没有尽头的,任何人都能找到每一件事情里边的是非。关键是哪一种方式更自然、更适合下一步的复杂情况,也就是适用本人地方法为最好。
你用技术化的思维方式、认为“反正本人说‘先是左脚到达、然后右脚又到达”也完成了程序编写,那就是你的理解方式。而别人用更加符合自然的方式,可能说明别人此时更加注重实践、注重其它系统设计也需要相同的编程框架。
假设有一个位置是(2,1),另一个位置是(3,2),假设这是“积木系统”可以从位置1将积木移动到位置2,那么你所谓的“先设置x为3、后设置y为2”跟“先设置y为2、后设置x为3”有区别吗?
对于静态概念,那么“坐标位置”概念本身是x-y方式标识的,是不可分割的两部分,不区分谁先谁后。对于动态行为概念,那么才会去用各种真正对应于实际领域的方式去设计它。因此一般来说,对于动态行为,会使用”goto(2,1)”一个数据作为第一个操作数据,然后使用“goto(3,2)”作为第二个操作数据。
例如说,一个人从上海到达了北京,问一下这个事件跟“这个人的左脚先从上海到达了北京,然后他的右脚又从上海到达了北京”这样的描述有区别吗?假如你觉得没有区别,那么你可以继续去研究。但是别人假设预见到了你这种不靠谱、不真实的冗余表述方式“不可取”,你应该理解他以什么事实为本,而你以什么来对付(凑合)。你从他的角度去理解,而不是纠结什么事非。
任何东西的是非争议都是没有尽头的,任何人都能找到每一件事情里边的是非。关键是哪一种方式更自然、更适合下一步的复杂情况,也就是适用本人地方法为最好。
你用技术化的思维方式、认为“反正本人说‘先是左脚到达、然后右脚又到达”也完成了程序编写,那就是你的理解方式。而别人用更加符合自然的方式,可能说明别人此时更加注重实践、注重其它系统设计也需要相同的编程框架。
10
你是指代码1的 71 ~ 102 中的 set 代码段吗?
首先,属性访问器就是为这类需求设计的,假如仅仅是赋值、取值就没有必要加这个累赘了
在属性被访问时,调用某个方法,就好比是触发器。这是在普通不过的需求了,并且几乎所用面向对象的语言都提供有这个能力。只是称谓不同而已
当然,你也可以单写个方法去实现这个行为,估计这就是称不可取的人的做法
对于你目前的应用,完全是没有问题的:x 和 y 不会同时改变
但假如你改变规则,允许斜线方向移动
那么你这样做就有点问题(也就是 不可取 了)
首先,属性访问器就是为这类需求设计的,假如仅仅是赋值、取值就没有必要加这个累赘了
在属性被访问时,调用某个方法,就好比是触发器。这是在普通不过的需求了,并且几乎所用面向对象的语言都提供有这个能力。只是称谓不同而已
当然,你也可以单写个方法去实现这个行为,估计这就是称不可取的人的做法
对于你目前的应用,完全是没有问题的:x 和 y 不会同时改变
但假如你改变规则,允许斜线方向移动
那么你这样做就有点问题(也就是 不可取 了)