使用 C# 开发智能手机软件:推箱子(十)

转载 2007年10月10日 09:16:00
 这是“使用 C# 开发智能手机软件:推箱子”系列文章的第十篇。在这篇文章中,介绍 Common/DataFile.cs 源程序文件。这个源程序文件中包含密封类 DataFile,用来管理数据文件。

    上图是数据文件 konka.bxb 的结构图。该数据文件大小为 297 字节,包含三个关卡,各个关卡的大小分别为:“8x7”、“8x7”和“9x5”。内容如下:
    1. 文件头(32字节,图中的青色部分)。首先是保留的四个字节。然后是一个字节的数据文件版本号(目前为“2”)。接着是三个字节的标志(内容为 “BOX”)。接着是十六字节的组名(编码为“GB2312”,本数据文件中为“康佳”)。接着是总关数(Int32,四个字节,因为本组共有三个关卡, 所以内容为“3”)。最后是第一关起始地址位置(Int32,四个字节,本数据文件中的内容为“0x11D”)。
    2. 以关数据头(32字节,图中的绿色部分)开始的各关数据。首先是一个字节的关数据头开始标志(“@”)。然后是一个字节的标志(最低位:0:未通关,1: 已通关)。接着是通关总步数(Int32,四个字节)。接着是通关推箱子步数(Int32,四个字节)。接着是十四个字节的保留字段。接着是本关的宽度 (Int32,四个字节)。接着是本关的高度(Int32,四个字节)。最后是“本关宽度x本关高度”个字节的关数据,也就是说地图中每个单元格占一个字 节,取值范围是“0”到“7”,分别表示:0:地,1:槽,2:墙,3:砖,4:箱子放在地上,5:箱子放在槽上,6:工人站在地上,7:工人站在槽上。 注意,每一个关卡必须刚好有一个工人。
    3. 数据文件的最后是各关起始地址列表(“总关数x4”个字节,图中的黄色部分)。每关的起始地址均为四个字节(Int32),所以共有“总关数x4”个字节。
    密封类 DataFile 的源代码中有详细的注释,很容易看懂。
    1. InitMap 方法用来初始化地图。地图的大小是“(关的高度+2) x (关的宽度+2)”,这是为了在地图四周砌上围墙,以免搜索算法越出地图边界。
    2. DeleteLevel 方法用来删除指定的关。注意,删除关时并不删除关数据,只是将该关的起始地址从各关地址列表中删除,然后将文件缩短四个字节(因为各关地址列表在数据文件 的最后)。这样数据文件中就可能包含不需要的冗余数据。通过“菜单 -> 数据 -> 转换”,先“导出”,然后“导入”,可以消除冗余数据。
  1 using System;
  2 using System.IO;
  3 using System.Drawing;
  4 using System.Collections.Generic;
  5 using System.Windows.Forms;
  6 
  7 namespace Skyiv.Ben.PushBox.Common
  8 {
  9   // data/<group>.bxb 文件格式
 10   // 保留 ver(2) BOX 组名- 总关数 第1关起始地址位置
 11   // 0--3 4----- 5-7 8--23 24--27 28-------------31
 12   //
 13   // @ Flag 总步数 推箱子步数 保留- wide- high- data
 14   // 0 1--- 2----5 6--------9 10-23 24-27 28-31 32..
 15   // Flag: 最低位: 0:未通关 1:已通关
 16   //
 17   // 第1关起始地址 第2关起始地址 . 最后一关起始地址
 18   // 0-----------3 4-----------7 . (文件最后四字节)
 19   //
 20   // steps/<group><level>.bxs 文件格式见 Step.cs
 21   // 其中<level>为关数(1起始),最少四位,不足前补零
 22   //
 23   // text/<group>.bxa 文件格式
 24   // 0 - land             SPACE
 25   // 1 + slot             .
 26   // 2 # wall             #
 27   // 3 % brick            N/A
 28   // 4 x box on land      $
 29   // 5 X box on slot      *
 30   // 6 ( man on land      @
 31   // 7 ) man on slot      +  .XSB 文件格式
 32   // 第一行如果以!开头的话, 则为组名(不能超过16个字符)
 33   // 以:开头的行为通关步骤, 格式同(.bxs)文件
 34   // 以'开头的行为注释, 完全忽略
 35   // 各关之间必须以空行分隔
 36 
 37   /// <summary>
 38   /// 管理数据文件: *.bxb  *.bxa  *.bxs
 39   /// </summary>
 40   sealed class DataFile : IDisposable
 41   {
 42     const byte DataVersion = 2;       // 数据文件(.bxb)的版本
 43     const byte LevelFlag = (byte)'@'// 数据文件(.bxb)的关标志
 44     const char RemChar = '/'';        // 文本文件(.bxa)的注释
 45     const char StepsChar = ':';       // 文本文件(.bxa)的通关步骤
 46 
 47     FileStream fs;    // 数据文件基础流
 48     BinaryReader br;  // 数据文件读取器
 49     BinaryWriter bw;  // 数据文件写入器
 50     string groupName; // 当前组名称
 51     int[] addrs;      // 各关起始地址列表,最后一项为第1关起始地址位置
 52     byte[,] map;      // 当前关地图
 53     int maxLevel;     // 总关数
 54     Size levelSize;   // 当前关尺寸(以单元格为单位)
 55     Point worker;     // 当前工人位置(以单元格为单位)
 56     int mans;         // 工人数
 57     int boxs;         // 箱子数
 58     int slots;        // 槽数
 59     int tasks;        // 总任务数
 60     int boths;        // 已完成任务数
 61     bool isFinished;  // 是否曾经通关
 62     int movedSteps;   // 通关的总步数
 63     int pushedSteps;  // 通关的推箱子步数
 64     string fileName { get { return Path.GetFileNameWithoutExtension(fs.Name); } } // 数据文件主名
 65 
 66     public string GroupName { get { return groupName; } }
 67     public int MaxLevel { get { return maxLevel; } }
 68     public byte[,] Map { get { return map; } }
 69     public Size LevelSize { get { return levelSize; } }
 70     public bool IsFinished { get { return isFinished; } }
 71     public int MovedSteps { get { return movedSteps; } }
 72     public int PushedSteps { get { return pushedSteps; } }
 73     public Point Worker { get { return worker; } }
 74     public bool HasWorker { get { return mans != 0; } }
 75     public int Boxs { get { return boxs; } }
 76     public int Slots { get { return slots; } }
 77     public int Tasks { get { return tasks; } }
 78     public int Boths { get { return boths; } set { boths = value; } }
 79 
 80     /// <summary>
 81     /// 装入组数据
 82     /// </summary>
 83     /// <param name="name">组文件名</param>
 84     public void LoadGroup(string name)
 85     {
 86       Dispose();
 87       fs = new FileStream(Path.Combine(Pub.DataDirectory, name + Pub.DataExtName), FileMode.Open);
 88       br = new BinaryReader(fs, Pub.Encode);
 89       bw = new BinaryWriter(fs, Pub.Encode);
 90       br.ReadInt32(); // 保留
 91       if (br.ReadByte() != DataVersion) throw new Exception("数据文件版本错");
 92       byte[] bs = br.ReadBytes(3); // 数据文件标志:BOX
 93       for (int i = 0; i < bs.Length; i++if (bs[i] != "BOX"[i]) throw new Exception("数据文件标志错");
 94       bs = br.ReadBytes(16); // 组名
 95       for (int i = 0; i < bs.Length; i++if (bs[i] == 0) bs[i] = 32;
 96       groupName = Pub.Encode.GetString(bs, 0, bs.Length).Trim();
 97       if (groupName.Length == 0) groupName = fileName; // 如果数据文件中组名为空,则用数据文件主名代替
 98       maxLevel = br.ReadInt32(); // 总关数
 99       int addrPos = br.ReadInt32(); // 第1关起始地址位置
100       br.BaseStream.Seek(addrPos, SeekOrigin.Begin);
101       addrs = new int[maxLevel + 1]; // 各关起始地址列表,最后一项为第1关起始地址位置
102       for (int i = 0; i < maxLevel; i++) addrs[i] = br.ReadInt32();
103       addrs[maxLevel] = addrPos; // 第1关起始地址位置
104       if (addrPos + 4 * maxLevel != br.BaseStream.Length) throw new Exception("数据文件地址表必须位于数据最后");
105     }
106 
107     /// <summary>
108     /// 装入关数据
109     /// </summary>
110     /// <param name="level">关数</param>
111     public void LoadLevel(int level)
112     {
113       LoadLevelHead(level);
114       InitMap();
115       for (int i = 1; i <= levelSize.Height; i++)
116       {
117         for (int j = 1; j <= levelSize.Width; j++)
118         {
119           map[i, j] = br.ReadByte();
120           UpdateCounts(j, i, true);
121         }
122       }
123       if (mans != 1throw new Exception("读取关数据失败:必须刚好有一个工人");
124       tasks = Math.Min(boxs, slots);
125     }
126 
127     /// <summary>
128     /// 新建一关
129     /// </summary>
130     /// <param name="isCopy">是否复制当前关</param>
131     /// <param name="size">新建关的尺寸</param>
132     public void NewLevel(bool isCopy, Size size)
133     {
134       Size levelSizeOem = levelSize;
135       byte[,] mapOem = isCopy ? (byte[,])map.Clone() : null;
136       levelSize = size;
137       InitMap();
138       for (int i = 1; i <= levelSize.Height; i++)
139       {
140         for (int j = 1; j <= levelSize.Width; j++)
141         {
142           map[i, j] = (isCopy && i <= levelSizeOem.Height && j <= levelSizeOem.Width) ? mapOem[i, j] : Block.Land;
143           UpdateCounts(j, i, true);
144         }
145       }
146       if (mans != 1 && mans != 0throw new Exception("不能超过一个工人");
147       tasks = Math.Min(boxs, slots);
148     }
149 
150     /// <summary>
151     /// 初始化地图
152     /// </summary>
153     private void InitMap()
154     {
155       map = new byte[levelSize.Height + 2, levelSize.Width + 2];
156       for (int i = 0; i <= levelSize.Height + 1; i++) map[i, 0= map[i, levelSize.Width + 1= Block.Wall;
157       for (int j = 0; j <= levelSize.Width + 1; j++) map[0, j] = map[levelSize.Height + 1, j] = Block.Wall;
158       mans = boxs = slots = boths = 0;
159     }
160 
161     /// <summary>
162     /// 根据地图项目更新统计信息
163     /// </summary>
164     /// <param name="x">当前位置横坐标</param>
165     /// <param name="y">当前位置纵坐标</param>
166     /// <param name="isAdd">加或减</param>
167     public void UpdateCounts(int x, int y, bool isAdd)
168     {
169       int sign = isAdd ? 1 : -1;
170       if (Block.IsBox(map[y, x])) boxs += sign;
171       if (Block.IsSlot(map[y, x])) slots += sign;
172       if (Block.Box1 == map[y, x]) boths += sign;
173       if (Block.IsMan(map[y, x]))
174       {
175         mans += sign;
176         worker = isAdd ? new Point(x, y) : Point.Empty;
177       }
178     }
179 
180     /// <summary>
181     /// 装入关数据头
182     /// </summary>
183     /// <param name="level">关数</param>
184     void LoadLevelHead(int level)
185     {
186       if (level > maxLevel - 1throw new Exception(string.Format("当前关数({0})不能大于总关数({1})", level + 1, maxLevel));
187       br.BaseStream.Seek(addrs[level], SeekOrigin.Begin);
188       if (br.ReadByte() != LevelFlag) throw new Exception("关数据标志错");
189       isFinished = (br.ReadByte() & 1== 1// 是否曾经通关
190       movedSteps = br.ReadInt32(); // 通关的总步数
191       pushedSteps = br.ReadInt32(); // 通关的推箱子步数
192       br.ReadBytes(14); // 保留
193       levelSize.Width = br.ReadInt32();
194       levelSize.Height = br.ReadInt32();
195     }
196 
197     /// <summary>
198     /// 更新当前关数据
199     /// </summary>
200     /// <param name="level">关数</param>
201     /// <param name="steps">通关步骤</param>
202     /// <param name="pushs">推箱子步数</param>
203     public void SaveLevel(int level, Step[] steps, int pushs)
204     {
205       SaveLevelHead(level, steps.Length, pushs);
206       SaveLevelSteps(level, Pub.ToString(steps));
207       LoadLevelHead(level);
208     }
209 
210     /// <summary>
211     /// 更新当前关头数据
212     /// </summary>
213     /// <param name="level">关数</param>
214     /// <param name="moves">通关步数</param>
215     /// <param name="pushs">推箱子步数</param>
216     void SaveLevelHead(int level, int moves, int pushs)
217     {
218       if (level > maxLevel - 1throw new Exception("关数太大");
219       bw.BaseStream.Seek(addrs[level] + 1, SeekOrigin.Begin);
220       bw.Write((byte)1); // 是否曾经通关
221       bw.Write(moves); // 通关的总步数
222       bw.Write(pushs); // 通关的推箱子步数
223     }
224 
225     /// <summary>
226     /// 保存通关步骤
227     /// </summary>
228     /// <param name="level">关数</param>
229     /// <param name="steps">通关步骤</param>
230     void SaveLevelSteps(int level, string steps)
231     {
232       if (!Directory.Exists(Pub.StepsDirectory)) Directory.CreateDirectory(Pub.StepsDirectory);
233       Fcl.WriteAllText(GetStepsFileName(fileName, level), steps);
234     }
235 
236     /// <summary>
237     /// 给出通关步骤
238     /// </summary>
239     /// <param name="level">关数</param>
240     /// <returns>通关步骤</returns>
241     public string GetSteps(int level)
242     {
243       return GetSteps(fileName, level);
244     }
245 
246     string GetSteps(string name, int level)
247     {
248       return Fcl.ReadAllText(GetStepsFileName(name, level));
249     }
250 
251     string GetStepsFileName(string name, int level)
252     {
253       return Path.Combine(Pub.StepsDirectory, name + (level + 1).ToString("D4"+ Pub.StepsExtName);
254     }
255 
256     /// <summary>
257     ///  删除通关步骤文件
258     /// </summary>
259     /// <param name="level">关数</param>
260     private void DeleteStepsFile(int level)
261     {
262       // 虽然 File.Delete(): 删除指定的文件。如果指定的文件不存在,则不引发异常。 
263       // 但是: 如果指定的路径无效,还是会引发 DirectoryNotFoundException 异常。
264       // 所以需要先用 File.Exists() 判断一下文件是否存在
265       string name = GetStepsFileName(fileName, level);
266       if (File.Exists(name)) File.Delete(name);
267     }
268 
269     /// <summary>
270     /// 保存设计数据
271     /// </summary>
272     /// <param name="isNew">是否新建</param>
273     /// <param name="level">要保存的关数</param>
274     public void SaveDesign(bool isNew, int level)
275     {
276       if (isNew && level != maxLevel) throw new Exception("新建的关必须在最后一关之后");
277       bw.BaseStream.Seek(addrs[level], SeekOrigin.Begin);
278       WriteLevel(level, string.Empty); // 如果不是新建,则关尺寸不能比原来的大
279       if (isNew)
280       {
281         Fcl.Resize(ref addrs, addrs.Length + 1);
282         addrs[++maxLevel] = (int)bw.BaseStream.Position;
283         WriteAddrs();
284       }
285       DeleteStepsFile(level); // 删除通关步骤文件
286     }
287 
288     /// <summary>
289     /// 删除最后一关
290     /// </summary>
291     /// <param name="level">关数(必须是最后一关)</param>
292     public void DeleteLastLevel(int level)
293     {
294       if (level != maxLevel - 1throw new Exception("要删除的关必须是最后一关");
295       DeleteLevel(level);
296       DeleteStepsFile(level); // 删除通关步骤文件,如果被删除的关不是最后一关,以后各关的通关步骤文件就不对了
297     }
298 
299     /// <summary>
300     /// 删除指定的关
301     /// </summary>
302     /// <param name="level">关数</param>
303     void DeleteLevel(int level)
304     {
305       for (int i = level + 1; i <= maxLevel; i++) addrs[i - 1= addrs[i]; // 之后的关起始地址前移
306       --maxLevel; // 更新总关数
307       WriteAddrs();
308     }
309 
310     /// <summary>
311     /// 更新各关起始地址列表及总关数和第1关起始地址位置
312     /// </summary>
313     private void WriteAddrs()
314     {
315       bw.Seek(addrs[maxLevel], SeekOrigin.Begin);
316       for (int i = 0; i < maxLevel; i++) bw.Write(addrs[i]); // 各关起始地址
317       bw.BaseStream.SetLength(bw.BaseStream.Position); // 关起始地址列表位于数据文件最后, 用于删除关的情况
318       bw.Seek(24, SeekOrigin.Begin);
319       bw.Write(maxLevel); // 总关数
320       bw.Write(addrs[maxLevel]); // 第1关起始地址位置
321     }
322 
323     /// <summary>
324     /// 更新组名
325     /// </summary>
326     void WriteGroupName()
327     {
328       byte[] bs = new byte[16];
329       byte[] bn = Pub.Encode.GetBytes(groupName);
330       for (int i = 0; i < bs.Length && i < bn.Length; i++) bs[i] = bn[i];
331       for (int i = bn.Length; i < bs.Length; i++) bs[i] = 32;
332       bw.Seek(8, SeekOrigin.Begin);
333       bw.Write(bs); // 组名
334     }
335 
336     /// <summary>
337     /// 写关数据和通关步骤
338     /// 注意:调用本函数前必须定位到数据文件的正确位置
339     /// </summary>
340     /// <param name="level">关数</param>
341     /// <param name="steps">通关步骤</param>
342     /// <returns>本关的统计信息</returns>
343     string WriteLevel(int level, string steps)
344     {
345       bw.Write(LevelFlag); // 关标志
346       bw.Write((byte)(string.IsNullOrEmpty(steps) ? 0 : 1)); // 标志:是否已通关
347       bw.Write(steps.Length); // 总步数
348       bw.Write(GetPushSteps(steps)); // 推箱子步数
349       bw.Write(new byte[14]); // 保留
350       bw.Write(levelSize.Width); // 当前关宽度
351       bw.Write(levelSize.Height); // 当前关高度
352       mans = slots = boxs = 0;
353       int lands = 0, walls = 0, bricks = 0;
354       for (int i = 1; i <= levelSize.Height; i++)
355       {
356         for (int j = 1; j <= levelSize.Width; j++)
357         {
358           bw.Write(map[i, j]);
359           switch (map[i, j])
360           {
361             case Block.Land: lands++break;
362             case Block.Slot: slots++break;
363             case Block.Wall: walls++break;
364             case Block.Brick: bricks++break;
365             case Block.Box0: lands++; boxs++break;
366             case Block.Box1: slots++; boxs++break;
367             case Block.Man0: lands++; mans++break;
368             case Block.Man1: slots++; mans++break;
369           }
370         }
371       }
372       if (mans != 1) ErrorExit(true, level + 1"必须刚好有一个工人");
373       if (!string.IsNullOrEmpty(steps)) SaveLevelSteps(level, steps);
374       return string.Format("{1}: {2} {3} {4} {5} {6} {7} {8}{0}",
375         Fcl.NewLine, level + 1, Pub.ToString(levelSize), walls, bricks, lands, slots, boxs, steps.Length);
376     }
377 
378     /// <summary>
379     /// 根据通关步骤给出推箱子步数
380     /// </summary>
381     /// <param name="steps">通关步骤</param>
382     /// <returns>推箱子步数</returns>
383     int GetPushSteps(string steps)
384     {
385       int n = 0;
386       foreach (char c in steps) if (((Step)c).IsBox) n++;
387       return n;
388     }
389 
390     /// <summary>
391     /// 数据导入
392     /// </summary>
393     /// <param name="name">数据文件主名</param>
394     /// <param name="maxLevelSize">最大关尺寸</param>
395     /// <param name="tbxMsg">显示相关信息的文本框</param>
396     public void Import(string name, int maxLevelSize, TextBox tbxMsg)
397     {
398       try
399       {
400         tbxMsg.Text = string.Format("{1} => {2}{0}", Fcl.NewLine, name + Pub.TextExtName, name + Pub.DataExtName);
401         if (!Directory.Exists(Pub.DataDirectory)) Directory.CreateDirectory(Pub.DataDirectory);
402         using (StreamReader sr = new StreamReader(Path.Combine(Pub.TextDirectory, name + Pub.TextExtName), Pub.Encode))
403         {
404           Dispose();
405           fs = new FileStream(Path.Combine(Pub.DataDirectory, name + Pub.DataExtName), FileMode.Create, FileAccess.Write);
406           bw = new BinaryWriter(fs, Pub.Encode);
407           byte[] buf = new byte[32];
408           buf[4= DataVersion;
409           buf[5= (byte)'B';
410           buf[6= (byte)'O';
411           buf[7= (byte)'X';
412           bw.Write(buf);
413           map = new byte[maxLevelSize + 2, maxLevelSize + 2];
414           List<int> addrList = new List<int>(); // 各关起始地址列表,最后一项为第1关起始地址位置
415           addrList.Add((int)bw.BaseStream.Position); // 第1关起始地址
416           groupName = name; // 组名
417           int level = 0;
418           levelSize = Size.Empty;
419           string steps = ""// 通关步骤
420           bool isFirst = true;
421           for (int line = 1; ; line++)
422           {
423             string s = sr.ReadLine();
424             if (s != null) s = s.Trim();
425             if (line == 1 && s != null && s.Length > 0 && s[0== '!')
426             {
427               groupName = s.Substring(1).Trim();
428               tbxMsg.Text += "组名: [" + groupName + "]" + Fcl.NewLine;
429               continue;
430             }
431             if (isFirst)
432             {
433               isFirst = false;
434               tbxMsg.Text += "#: 宽x高 墙 砖 地 槽 箱 通关步数" + Fcl.NewLine;
435             }
436             if ((s == null || s.Length == 0&& levelSize != Size.Empty)
437             {
438               tbxMsg.Text += WriteLevel(level, steps);
439               addrList.Add((int)bw.BaseStream.Position); // 下一关起始地址
440               level++;
441               levelSize = Size.Empty;
442               steps = "";
443             }
444             if (s == nullbreak;
445             if (s.Length == 0 || s[0== RemChar) continue;
446             if (s[0== StepsChar)
447             {
448               steps = s.Substring(1).Trim(); // 通关步骤
449               continue;
450             }
451             levelSize.Height++;
452             if (levelSize.Height == 1) levelSize.Width = s.Length;
453             else if (levelSize.Width != s.Length) ErrorExit(false, line, "宽度不齐");
454             if (levelSize.Width > maxLevelSize) ErrorExit(false, line, GetMessage("宽度太大"true));
455             if (levelSize.Height > maxLevelSize) ErrorExit(false, line, GetMessage("高度太大"true));
456             for (int i = 0; i < levelSize.Width; i++)
457               if (!Block.IsBlock(map[levelSize.Height, i + 1= Block.GetByte(s[i])))
458                 ErrorExit(false, line, "非法字符:[" + s[i] + "]");
459           }
460           addrs = addrList.ToArray();
461           maxLevel = level;
462           WriteAddrs();
463           WriteGroupName();
464         }
465       }
466       catch (OutOfMemoryException ex)
467       {
468         throw new Exception(GetMessage("内存不足"false), ex);
469       }
470       finally
471       {
472         Dispose();
473       }
474       tbxMsg.Text += "导入完成";
475     }
476 
477     string GetMessage(string msg1, bool isIncrease)
478     {
479       return msg1 + ",请在“菜单 -> 选项”对话框中" + (isIncrease ? "增加" : "减少"+"“最大关尺寸”";
480     }
481 
482     /// <summary>
483     /// 数据导出
484     /// </summary>
485     /// <param name="name">数据文件主名</param>
486     /// <param name="tbxMsg">显示相关信息的文本框</param>
487     public void Export(string name, TextBox tbxMsg)
488     {
489       try
490       {
491         tbxMsg.Text = string.Format("{1} => {2}{0}", Fcl.NewLine, name + Pub.DataExtName, name + Pub.TextExtName);
492         LoadGroup(name);
493         if (!Directory.Exists(Pub.TextDirectory)) Directory.CreateDirectory(Pub.TextDirectory);
494         using (StreamWriter sw = new StreamWriter(
495           Path.Combine(Pub.TextDirectory, name + Pub.TextExtName), false, Pub.Encode))
496         {
497           sw.WriteLine("! {0}", groupName);
498           tbxMsg.Text += "组名: [" + groupName + "]" + Fcl.NewLine;
499           tbxMsg.Text += "#: 宽x高 总任务数 通关步数" + Fcl.NewLine;
500           for (int level = 0; level < maxLevel; level++)
501           {
502             LoadLevel(level);
503             sw.WriteLine("{0}[{1}]", RemChar, level + 1); // 注释:第几关
504             for (int y = 0; y < levelSize.Height; y++)
505             {
506               for (int x = 0; x < levelSize.Width; x++) sw.Write(Block.GetChar(map[y + 1, x + 1]));
507               sw.WriteLine();
508             }
509             string steps = GetSteps(name, level); // 通关步骤
510             if (!string.IsNullOrEmpty(steps)) sw.WriteLine(StepsChar + steps);
511             sw.WriteLine();
512             tbxMsg.Text += string.Format("{1}: {2} {3} {4}{0}",
513               Fcl.NewLine, level + 1, Pub.ToString(levelSize), tasks, steps.Length);
514           }
515         }
516       }
517       finally
518       {
519         Dispose();
520       }
521       tbxMsg.Text += "导出完成";
522     }
523 
524     void ErrorExit(bool isLevel, int idx, string msg)
525     {
526       throw new Exception(string.Format("错误:第{0}{1}:{2}", idx, isLevel ? "" : "", msg));
527     }
528 
529     public void Dispose()
530     {
531       if (br != null) br.Close();
532       if (bw != null) bw.Close();
533       if (fs != null) fs.Close();
534       br = null;
535       bw = null;
536       fs = null;
537     }
538   }
539 }
540
 

15款顶级的开源人工智能工具推荐

转自:http://os.51cto.com/art/201609/517610.htm 人工智能是技术研究领域最炙手可热的领域之一。IBM、谷歌、微软、Facebook和亚马逊等公司正投入巨资...
  • qazwsxwtc
  • qazwsxwtc
  • 2017年01月17日 10:48
  • 1544

人工智能-遗传算法解决推箱子问题现实

一、研究背景    推箱子游戏中的路径查找问题—给定一方格,求两点最短距离。    传统求两点最短路径的算法有:      1.通用的搜索算法      2.解决无负权边的带权有向图的单源最短路问题的...
  • xj2419174554
  • xj2419174554
  • 2013年12月18日 23:16
  • 3668

人工智能-遗传算法解决推箱子问题现实

一、研究背景    推箱子游戏中的路径查找问题—给定一方格,求两点最短距离。    传统求两点最短路径的算法有:      1.通用的搜索算法      2.解决无负权边的带权有向图的单源最短...
  • u014568921
  • u014568921
  • 2015年05月19日 10:43
  • 1228

使用 C# 开发智能手机软件:推箱子(四)

这是“使用 C# 开发智能手机软件:推箱子”系列文章的第四篇。在这篇文章中,介绍 Common/FindPath.cs 源程序文件。 using System; using System....
  • u013948190
  • u013948190
  • 2015年07月26日 11:13
  • 584

使用 C# 开发智能手机软件:推箱子(二十三)

这是“使用 C# 开发智能手机软件:推箱子” 系列文章的第二十三篇。在这篇文章中,介绍 Window/MainForm.Common.cs 源程序文件。这个源程序文件是 MainForm 类的一部分,...
  • u013948190
  • u013948190
  • 2015年07月26日 11:29
  • 386

使用 C# 开发智能手机软件:推箱子(十二)

这是“使用 C# 开发智能手机软件:推箱子”系列文章的第十二篇。在这篇文章中,介绍 Window/AboutDlg.cs 源程序文件。这个源程序文件包含 AboutDlg 类,该类继承自 Syst...
  • u013948191
  • u013948191
  • 2015年07月26日 11:18
  • 656

使用 C# 开发智能手机软件:推箱子(十三)

这是“使用 C# 开发智能手机软件:推箱子”系列文章的第十三篇。在这篇文章中,介绍 Window/TopicDlg.cs 源程序文件。这个源程序文件包含 TopicDlg 类,该类继承自 Syst...
  • u013948187
  • u013948187
  • 2015年07月26日 11:28
  • 511

使用 C# 开发智能手机软件:推箱子(二)

在上篇文章“使用 C# 开发智能手机软件:推箱子(一)”中,我对推箱子程序作了总体介绍。这次,我先介绍 Common/Fcl.cs 源程序文件。  1 using System;  2 u...
  • u013948191
  • u013948191
  • 2015年07月26日 11:11
  • 378

使用 C# 开发智能手机软件:推箱子(二十二)

这是“使用 C# 开发智能手机软件:推箱子” 系列文章的第二十二篇。在这篇文章中,介绍 Window/MainForm.Replay.cs 源程序文件。这个源程序文件是 MainForm 类的一部分,...
  • u013948190
  • u013948190
  • 2015年07月26日 11:29
  • 710

使用 C# 开发智能手机软件:推箱子(三)

这是“使用 C# 开发智能手机软件:推箱子”系列文章的第三篇。在这篇文章中,介绍 Common/Block.cs 源程序文件。   1 namespace Skyiv.Ben.PushBox...
  • u013948187
  • u013948187
  • 2015年07月26日 11:12
  • 594
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:使用 C# 开发智能手机软件:推箱子(十)
举报原因:
原因补充:

(最多只允许输入30个字)