在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
概述在上一篇随笔“【算法】从推箱子的解答步骤还原关卡地图”中,我给出一个控制台应用程序,将 LURD 数据转换为 XSB 数据。为了方便使用,我编写了一个 ASP.NET 网页实现从推箱子的解答步骤还原关卡地图:Sokoban: Lurd to Xsb,如下所示: 源程序代码首先是 lurd2xsb.aspx 源程序文件: 01: <%@ Page Language="C#" inherits="Skyiv.Ben.Web.Lurd2xsbPage" %> 02: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 03: <html xmlns="http://www.w3.org/1999/xhtml" > 04: <head runat="server"> 05: <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 06: <title>Sokoban: Lurd to Xsb</title> 07: </head> 08: <body> 09: <form id="form1" runat="server"> 10: <asp:Button Text="Home" OnClick="BtnUriHome_Click" runat="server" /> 11: <asp:Button Text="Start" OnClick="BtnSubmit_Click" runat="server" /> 12: <a href="http://sokoban.ws/xsb_lurd.php" target="_blank">Introduction to XSB and LURD</a> 13: <a href="http://sokoban.ws/sokoplayer/SokoPlayer_HTML5.php" target="_blank">SokoPlayer HTML5</a> 14: <a href="http://sokoban.ws/utility/SokoEditor.php" target="_blank">SokoEditor HTML5</a> 15: <a href="http://www.cnblogs.com/skyivben/archive/2011/07/03/2096801.html" target="_blank">Algorithm</a> 16: <hr /> 17: <div> 18: <table><tbody><tr/><td> 19: <asp:TextBox Runat="Server" Id="tbxLurd" MaxLength="102400" TextMode="MultiLine" Columns="50" Rows="11" 20: Text="rrurruulllulldRRRRlllldlluRRurrddLLrDururrurrdLddlldlLuululldRRuurrdLLrrrRurrdLLrddlldlluUruRRlldlluRRlddddlluRdrU"/> 21: <br /> 22: <asp:TextBox Runat="Server" Id="tbxMoves" ReadOnly="True" Columns="57" BackColor="#CFECEC"/></td><td> 23: <asp:TextBox Runat="Server" Id="tbxXsb" ReadOnly="True" TextMode="MultiLine" Columns="60" Rows="12" Wrap="False" BackColor="#CFECEC"/> 24: </td></tr></tbody></table> 25: <br /> 26: <img src="xsbbitmap.aspx" /><hr /> 27: I would like to thank <a href="http://borgar.net/s/2009/07/sokoban/" target="_blank">Borgar 28: Þorsteinsson</a> for his kind permission using the elegant skin designed by him. 29: </div> 30: </form> 31: </body> 32: </html> 注意上述源程序文件中第 26 行使用了 HTML 的 <img /> 标签,而没有使用 ASP.NET 的 <asp:Image /> 控件。这是因为后者只能通过其 ImageUri 属性来显示图像。而我们的图像是根据 Xsb 数据动态生成的,这就不符合要求了。相应的 lurd2xsb.aspx.cs 源程序文件如下所示: 01: using System; 02: using System.IO; 03: using System.Web.UI; 04: using System.Web.UI.WebControls; 05: using Skyiv.Ben.Game; 06: 07: namespace Skyiv.Ben.Web 08: { 09: public class Lurd2xsbPage : Page 10: { 11: protected TextBox tbxLurd; 12: protected TextBox tbxMoves; 13: protected TextBox tbxXsb; 14: 15: public void BtnUriHome_Click(object sender, EventArgs e) 16: { 17: Response.Redirect("~/"); 18: } 19: 20: public void BtnSubmit_Click(object sender, EventArgs e) 21: { 22: try 23: { 24: var runer = new Lurd2Xsb(); 25: var xsb = runer.GetXsbFromLurd(tbxLurd.Text); 26: tbxMoves.Text = string.Format("{0:N0} moves, {1:N0} pushes", runer.Moves, runer.Pushes); 27: tbxXsb.Text = xsb; 28: Session["map"] = Xsb2Bitmap.GetMap(xsb); 29: } 30: catch (Exception ex) 31: { 32: tbxXsb.Text = "Error: " + ex.Message; 33: } 34: } 35: } 36: } 上述源程序代码的第 28 行将表示关卡地图的二维的字节数组存放到 Session 中,以便后面动态生成关卡地图的图像时使用。上述源程序第 24 行中使用到的 Skyiv.Ben.Game.Lurd2Xsb 类位于 lurd2xsb.cs 源程序文件中: 001: using System; 002: using System.Text; 003: using System.Drawing; 004: 005: namespace Skyiv.Ben.Game 006: { 007: public sealed class Lurd2Xsb 008: { 009: public int Moves { get { return Steps + Pushes; } } 010: public int Pushes { get; private set; } 011: public int Steps { get; private set; } 012: void SetBrick(byte[,] map, int x, int y) { map[x, y] = 8; } 013: void SetFloor(byte[,] map, Point pt) { map[pt.X, pt.Y] |= 1; } 014: void SetGoal(byte[,] map, Point pt) { map[pt.X, pt.Y] |= 2; } 015: void SetBox(byte[,] map, Point pt) { map[pt.X, pt.Y] |= 4; } 016: void UnsetBox(byte[,] map, Point pt) { map[pt.X, pt.Y] &= 0xFB; } 017: bool HasBox(byte[,] map, int x, int y) { return (map[x, y] & 4) != 0; } 018: bool IsGoal(byte[,] map, int x, int y) { return (map[x, y] & 2) != 0; } 019: bool IsFloor(byte[,] map, int x, int y) { return (map[x, y] & 1) != 0; } 020: bool IsBrick(byte[,] map, int x, int y) { return map[x, y] == 8; } 021: bool IsWall(byte[,] map, int x, int y) { return map[x, y] == 0; } 022: bool IsBrickOrWall(byte[,] map, int x, int y) { return IsBrick(map, x, y) || IsWall(map, x, y); } 023: 024: void MarkBrick(byte[,] map, int x, int y) 025: { 026: if (!IsWall(map, x, y)) return; 027: if (!IsBrickOrWall(map, x - 1, y - 1)) return; 028: if (!IsBrickOrWall(map, x - 1, y + 1)) return; 029: if (!IsBrickOrWall(map, x + 1, y - 1)) return; 030: if (!IsBrickOrWall(map, x + 1, y + 1)) return; 031: if (!IsBrickOrWall(map, x - 1, y)) return; 032: if (!IsBrickOrWall(map, x + 1, y)) return; 033: if (!IsBrickOrWall(map, x, y - 1)) return; 034: if (!IsBrickOrWall(map, x, y + 1)) return; 035: SetBrick(map, x, y); 036: } 037: 038: int GetX(byte[,] map, int y0, int y1, int x, int x2) 039: { 040: for (var y = y0; y < y1; y++) 041: if (!IsBrick(map, x, y)) return x; 042: return x2; 043: } 044: 045: int GetY(byte[,] map, int x0, int x1, int y, int y2) 046: { 047: for (var x = x0; x < x1; x++) 048: if (!IsBrick(map, x, y)) return y; 049: return y2; 050: } 051: 052: Rectangle GetRectangle(byte[,] map) 053: { 054: int x0 = 1, y0 = 1, x1 = map.GetLength(0) - 2, y1 = map.GetLength(1) - 2; 055: x0 = GetX(map, y0, y1, x0, x0 + 1); 056: x1 = GetX(map, y0, y1, x1, x1 - 1); 057: y0 = GetY(map, x0, x1, y0, y0 + 1); 058: y1 = GetY(map, x0, x1, y1, y1 - 1); 059: return Rectangle.FromLTRB(x0, y0, x1, y1); 060: } 061: 062: string GetXsb(byte[,] map, Point man) 063: { 064: var rect = GetRectangle(map); 065: for (var y = rect.Top; y <= rect.Bottom; y++) 066: for (var x = rect.Left; x <= rect.Right; x++) 067: MarkBrick(map, x, y); 068: rect = GetRectangle(map); 069: var xsb = new StringBuilder(); 070: for (var y = rect.Top; y <= rect.Bottom; y++) 071: { 072: for (var x = rect.Left; x <= rect.Right; x++) 073: { 074: if (IsGoal(map, x, y)) 075: { 076: if (man.X == x && man.Y == y) xsb.Append('+'); 077: else if (HasBox(map, x, y)) xsb.Append('*'); 078: else xsb.Append('.'); 079: } 080: else if (IsFloor(map, x, y)) 081: { 082: if (man.X == x && man.Y == y) xsb.Append('@'); 083: else if (HasBox(map, x, y)) xsb.Append('$'); 084: else xsb.Append('-'); 085: } 086: else if (IsBrick(map, x, y)) xsb.Append('_'); 087: else xsb.Append('#'); 088: } 089: xsb.AppendLine(); 090: } 091: return xsb.ToString(); 092: } 093: 094: void LeftOrRight(byte[,] map, ref Point man, int step) 095: { 096: man.X += step; 097: if (HasBox(map, man.X, man.Y)) UnsetBox(map, man); 098: else SetGoal(map, man); 099: man.X -= step; 100: SetBox(map, man); 101: man.X -= step; 102: SetFloor(map, man); 103: } 104: 105: void UpOrDown(byte[,] map, ref Point man, int step) 106: { 107: man.Y += step; 108: if (HasBox(map, man.X, man.Y)) UnsetBox(map, man); 109: else SetGoal(map, man); 110: man.Y -= step; 111: SetBox(map, man); 112: man.Y -= step; 113: SetFloor(map, man); 114: } 115: 116: byte[,] GetMap(string lurd, Size size, ref Point man) 117: { 118: var map = new byte[size.Width, size.Height]; 119: SetFloor(map, man); 120: for (var i = lurd.Length - 1; i >= 0; i--) 121: { 122: switch (lurd[i]) 123: { 124: case 'l': man.X++; SetFloor(map, man); Steps++; break; 125: case 'r': man.X--; SetFloor(map, man); Steps++; break; 126: case 'u': man.Y++; SetFloor(map, man); Steps++; break; 127: case 'd': man.Y--; SetFloor(map, man); Steps++; break; 128: case 'L': LeftOrRight(map, ref man, -1); Pushes++; break; 129: case 'R': LeftOrRight(map, ref man, 1); Pushes++; break; 130: case 'U': UpOrDown(map, ref man, -1); Pushes++; break; 131: case 'D': UpOrDown(map, ref man, 1); Pushes++; break; 132: } 133: } 134: return map; 135: } 136: 137: Rectangle GetBoundary(string lurd) 138: { 139: var boundary = new Rectangle(0, 0, 1, 1); 140: var man = new Point(); 141: for (var i = lurd.Length - 1; i >= 0; i--) 142: { 143: switch (lurd[i]) 144: { 145: case 'l': case 'L': man.X++; if (boundary.Right <= man.X) boundary.Width++; break; 146: case 'r': case 'R': man.X--; if (boundary.Left > man.X) { boundary.Width++; boundary.X--; } break; 147: case 'u': case 'U': man.Y++; if (boundary.Bottom <= man.Y) boundary.Height++; break; 148: case 'd': case 'D': man.Y--; if (boundary.Top > man.Y) { boundary.Height++; boundary.Y--; } break; 149: } 150: } 151: return boundary; 152: } 153: 154: public string GetXsbFromLurd(string lurd) 155: { 156: var boundary = GetBoundary(lurd); 157: var size = new Size(boundary.Width + 6, boundary.Height + 6); 158: var man = new Point(3 - boundary.X, 3 - boundary.Y); 159: var map = GetMap(lurd, size, ref man); 160: return GetXsb(map, man); 161: } 162: } 163: } 这个源程序文件已经在上一篇随笔“【算法】从推箱子的解答步骤还原关卡地图”中介绍过了。下面是 xsb2bitmap.cs 源程序文件: 01: using System; 02: using System.Drawing; 03: using System.Reflection; 04: 05: namespace Skyiv.Ben.Game 06: { 07: public sealed class Xsb2Bitmap 08: { 09: static Bitmap img8; 10: static Size boxSize; 11: byte[,] map; 12: 13: static Xsb2Bitmap() 14: { 15: img8 = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("PushBox24.png")); 16: boxSize = new Size(img8.Width / 8, img8.Height); 17: } 18: 19: public static byte[,] GetMap(string xsb) 20: { 21: var lines = xsb.Split(default(Char[]), StringSplitOptions.RemoveEmptyEntries); 22: var map = new byte[lines[0].Length, lines.Length]; 23: for (var y = 0; y < map.GetLength(1); y++) 24: for (var x = 0; x < map.GetLength(0); x++) 25: map[x, y] = GetByte(lines[y][x]); 26: return map; 27: } 28: 29: static byte GetByte(char c) 30: { 31: switch (c) 32: { 33: case ' ': 34: case '-': return 0; 35: case '.': return 1; 36: case '#': return 2; 37: case '_': return 3; 38: case '$': return 4; 39: case '*': return 5; 40: case '@': return 6; 41: case '+': return 7; 42: } 43: throw new Exception("invalid XSB char:[" + c + "]"); 44: } 45: 46: public Xsb2Bitmap(byte[,] map) 47: { 48: this.map = map; 49: } 50: 51: public Bitmap GetBitmap() 52: { 53: if (map == null) return new Bitmap(1, 1); 54: var img = new Bitmap(boxSize.Width * map.GetLength(0), boxSize.Height * map.GetLength(1)); 55: var graphics = Graphics.FromImage(img); 56: for (var y = 0; y < map.GetLength(1); y++) 57: for (var x = 0; x < map.GetLength(0); x++) 58: DrawBox(graphics, map[x, y], x * boxSize.Width, y * boxSize.Height); 59: return img; 60: } 61: 62: void DrawBox(Graphics graphics, int idx, int x, int y) 63: { 64: graphics.DrawImage(img8, x, y, new Rectangle(idx * boxSize.Width, 0, boxSize.Width, boxSize.Height), GraphicsUnit.Pixel); 65: } 66: } 67: } 上述源程序有两个用途:
下面就是用于动态生成图像的 xsbbitmap.aspx 源程序文件了: 01: <%@ Page Language="C#" inherits="Skyiv.Ben.Web.XsbBitmapPage" %> 02: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 03: <html xmlns="http://www.w3.org/1999/xhtml"> 04: <head runat="server"> 05: <title>Xsb Bitmap</title> 06: </head> 07: <body> 08: <form id="form1" runat="server"> 09: </form> 10: </body> 11: </html> 对应的 xsbbitmap.aspx.cs 源程序文件如下所示: 01: using System; 02: using System.IO; 03: using System.Drawing.Imaging; 04: using System.Web.UI; 05: using System.Web.UI.WebControls; 06: using Skyiv.Ben.Game; 07: 08: namespace Skyiv.Ben.Web 09: { 10: public class XsbBitmapPage : Page 11: { 12: public void Page_Load(object sender, EventArgs e) 13: { 14: CreateImage((byte[,])Session["map"]); 15: } 16: 17: void CreateImage(byte[,] map) 18: { 19: Session["map"] = null; 20: var img = new Xsb2Bitmap(map).GetBitmap(); 21: try 22: { 23: var ms = new MemoryStream(); 24: img.Save(ms, ImageFormat.Png); 25: Response.ClearContent(); 26: Response.ContentType = "image/Png"; 27: Response.BinaryWrite(ms.ToArray()); 28: } 29: finally 30: { 31: img.Dispose(); 32: Response.End(); 33: } 34: } 35: } 36: } 上述源程序第 14 行从前面赋值的 Session 取得表示关卡地图的二维字节数组,然后在第 20 行调用 Xsb2Bitmap 类的 GetBitmap 方法得到一个 Bitmap 图像,接着就是通过 Page 类的 |
请发表评论