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 不会同时改变
但假如你改变规则,允许斜线方向移动
那么你这样做就有点问题(也就是 不可取 了)