using System; using System.Drawing; using System.Collections; public class YRectangle { // //////////////////////////////////////////////////////////////////////////// private int x, y, width, height; public enum Corner { TopLeft, TopRight, BottomLeft, BottomRight }; public enum Edge { Left, Right, Top, Bottom }; // //////////////////////////////////////////////////////////////////////////// public int Left { get { return x; } set { x = value; } } public int Right { get { return x + width; } } public int Top { get { return y; } set { y = value; } } public int Bottom { get { return y + height; } } public int Width { get { return width; } set { width = value; } } public int Height { get { return height; } set { height = value; } } // //////////////////////////////////////////////////////////////////////////// public YRectangle(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; } // //////////////////////////////////////////////////////////////////////////// public bool IsLeft(Point p) { return p.X <= Left; } public bool IsRight(Point p) { return p.X >= Right; } public bool IsAbove(Point p) { return p.Y <= Top; } public bool IsBelow(Point p) { return p.Y >= Bottom; } public bool ContainsNoBoundary(Point p) { return Left < p.X && p.X < Right && Top < p.Y && p.Y < Bottom; } public bool Contains(Point p) { return Left <= p.X && p.X <= Right && Top <= p.Y && p.Y <= Bottom; } // //////////////////////////////////////////////////////////////////////////// private int ExamineCross(Point p, Point q, int x0, int y0, bool vertical) { int z, z0; if(vertical) { if((p.X - x0) * (q.X - x0) > 0) return 0; z0 = Height * (q.X - p.X); } else { if((p.Y - y0) * (q.Y - y0) > 0) return 0; z0 = Width * (p.Y - q.Y); } z = (q.Y-p.Y) * x0 - (q.X-p.X) * y0 + (p.Y * q.X - p.X * q.Y); if(z0 < 0) z = -z; if(0 < z && z < Math.Abs(z0)) return 2; if(z == 0 || z == Math.Abs(z0)) return 1; return 0; } public bool CrossedBy(Point p, Point q) { int n = 0; n += ExamineCross(p, q, Left, Top, true); n += ExamineCross(p, q, Right, Top, true); n += ExamineCross(p, q, Left, Top, false); n += ExamineCross(p, q, Left, Bottom, false); if(ContainsNoBoundary(p) && !Contains(q)) n += 2; if(ContainsNoBoundary(q) && !Contains(p)) n += 2; return n >= 4; } // //////////////////////////////////////////////////////////////////////////// public Point GetCornerPosition(Corner corner) { switch(corner) { case Corner.TopLeft: return new Point(Left, Top); case Corner.BottomLeft: return new Point(Left, Bottom); case Corner.TopRight: return new Point(Right, Top); case Corner.BottomRight: return new Point(Right, Bottom); } // ここまで来ることはないが、とりあえず左上隅を返す return new Point(Left, Top); } // //////////////////////////////////////////////////////////////////////////// } public class ObstacleArea { // //////////////////////////////////////////////////////////////////////////// private OrganismState creature; private YRectangle rect; // //////////////////////////////////////////////////////////////////////////// public OrganismState Creature { get { return creature; } } // //////////////////////////////////////////////////////////////////////////// public ObstacleArea(OrganismState aCreature, int myCellRadius) { creature = aCreature; int r = aCreature.CellRadius + myCellRadius + 1; int x = (aCreature.GridX - r) * 8; int y = (aCreature.GridY - r) * 8; int size = (2 * r + 1) * 8; rect = new YRectangle(x, y, size, size); } // //////////////////////////////////////////////////////////////////////////// public bool Contains(Point p) { return rect.Contains(p); } public bool ContainsNoBoundary(Point p) { return rect.ContainsNoBoundary(p); } public bool CrossedBy(Point p, Point q) { return rect.CrossedBy(p, q); } public IList GetBypassCorners(Point p) { bool left, right, above, below; left = rect.IsLeft(p); right = rect.IsRight(p); above = rect.IsAbove(p); below = rect.IsBelow(p); if(!(left || right || above || below)) { int dx = 2 * p.X - (rect.Left + rect.Right); int dy = 2 * p.Y - (rect.Top + rect.Bottom); if(Math.Abs(dx) < Math.Abs(dy)) { above = (dy <= 0); below = (dy > 0); } else { left = (dx <= 0); right = (dx > 0); } } IList cornerList = new ArrayList(); if(left ^ above) cornerList.Add(YRectangle.Corner.TopLeft); if(left ^ below) cornerList.Add(YRectangle.Corner.BottomLeft); if(right ^ above) cornerList.Add(YRectangle.Corner.TopRight); if(right ^ below) cornerList.Add(YRectangle.Corner.BottomRight); return cornerList; } public Point GetCornerPosition(YRectangle.Corner corner) { return rect.GetCornerPosition(corner); } // //////////////////////////////////////////////////////////////////////////// } abstract public class KYAnimal : KAnimal { // //////////////////////////////////////////////////////////////////////////// private Point destination; private OrganismState target = null; private int moveSpeed; private ArrayList foundObstacleAreas; // //////////////////////////////////////////////////////////////////////////// static private double Distance(Point p, Point q) { int dx = p.X - q.X; int dy = p.Y - q.Y; return Math.Sqrt(dx * dx + dy * dy); } protected void UpdateObstacleArea() { foundObstacleAreas = new ArrayList(); foreach(OrganismState os in foundCreatures) { if(target != null) { if(os.ID == target.ID) continue; } foundObstacleAreas.Add(new ObstacleArea(os, State.CellRadius)); } } // //////////////////////////////////////////////////////////////////////////// protected Point GetBypassPosition (ObstacleArea obstacle, YRectangle.Corner corner, int depth) { Point cPoint = obstacle.GetCornerPosition(corner); if(depth <= 0) return cPoint; foreach(ObstacleArea a in foundObstacleAreas) { if(a != obstacle && a.Contains(cPoint)) { return(GetBypassPosition(a, corner, depth - 1)); } } return cPoint; } protected Point FindNextPosition(Point dept, Point dest, int depth) { // 一番近くにある障害物を探索する ObstacleArea obstacle = null; double minDist = Double.PositiveInfinity; foreach(ObstacleArea a in foundObstacleAreas) { if(a.CrossedBy(dept, dest) || a.ContainsNoBoundary(dept)) { double dist = Distance(dept, a.Creature.Position); if(dist < minDist) { obstacle = a; minDist = dist; } } } // 障害物がなければ目的地に直行 if(obstacle == null || depth <= 0) { return dest; } // 経由点の候補を2つに絞る IList cornerList; if(obstacle.ContainsNoBoundary(dept)) { cornerList = obstacle.GetBypassCorners(dept); foundObstacleAreas.Remove(obstacle); } else { cornerList = obstacle.GetBypassCorners(dest); } // そのうちで近いほうの点を経由点とする Point p = GetBypassPosition(obstacle, (YRectangle.Corner)cornerList[0], 10); Point q = GetBypassPosition(obstacle, (YRectangle.Corner)cornerList[1], 10); if(Distance(dept, p) < Distance(dept, q)) { return FindNextPosition(dept, p, depth - 1); } else { return FindNextPosition(dept, q, depth - 1); } } // //////////////////////////////////////////////////////////////////////////// private void Move() { if(Position == destination) { StopMoving(); return; } Point bypass = FindNextPosition(Position, destination, 5); bypass = RangeCheck(bypass); BeginMoving(new MovementVector(bypass, moveSpeed)); } private void ChangeMovement(Point point, int speed) { point = RangeCheck(point); speed = SpeedCheck(speed); if(point != destination || speed != moveSpeed) { destination = point; moveSpeed = speed; Move(); } } // //////////////////////////////////////////////////////////////////////////// override protected void BeginMoving(Point point, int speed) { target = null; ChangeMovement(point, speed); } override protected void BeginMoving(Point point) { target = null; ChangeMovement(point, FavSpeed); } override protected void BeginMoving(OrganismState target, int speed) { this.target = target; ChangeMovement(target.Position, speed); } override protected void BeginMoving(OrganismState target) { this.target = target; ChangeMovement(target.Position, FavSpeed); } // //////////////////////////////////////////////////////////////////////////// override protected void LoadEvent(object sender, LoadEventArgs e) { try { base.LoadEvent(sender, e); UpdateObstacleArea(); if(!destination.IsEmpty) { Move(); } } catch(Exception exc) { WriteTrace(exc); } } override protected void MoveCompletedEvent(object sender, MoveCompletedEventArgs e) { try { if(e.Reason == ReasonForStop.Blocked) { OrganismState os = LookFor(e.BlockingOrganism); if(os != null) { if(IsBrunt(os) && CanAttack(os as AnimalState)) { Brunt = os as AnimalState; if(WannaAttack(Brunt)) { BeginAttacking(Brunt); } return; } if(IsFood(os) && WithinEatingRange(os)) { Food = os; if(WannaEat(os)) { BeginEating(os); } return; } } } else if(chase != null) { if(WithinAttackingRange(chase)) { chase = null; } else { BeginChase(chase); } } } catch(Exception exc) { WriteTrace(exc); } } // //////////////////////////////////////////////////////////////////////////// }