---
# System prepended metadata

title: エイチーム 1Dayインターン

---

# エイチーム 1Dayインターン
INVINCIBLE
## メモ

* 回復は強いからとるべき
* メイン機は移動が遅い
* 逃げる時は細かく曲がるようにすると良さそう。壁に沿わせる？

## classes
```csharp
 
enum MODE
{ // 巡回、接敵、アイテム、救援
    PATROL, ENCOUNT, ITEM, HELP
}
 ```
 
## member var.
```csharp
static int [,] map;
List<CharacterModel.Data> characterData; // キャラクター情報のリスト
List<CharacterModel.Data> enemyData; // 敵情報のリスト
int middleInterval; // 中距離攻撃のインターバル 30フレームで再使用可能
int shortInterval; // 短距離攻撃のインターバル 180フレームで再使用可能
int invincibleInterval; // 無敵状態のインターバル 900フレームで再使用可能
mode [] mode; //各機体のモード管理
```

## member func.

```csharp
// A-starアルゴリズムでposからtoまでの最短経路をリストで返す
private List<Common.MOVE_TYPE> AStar(Vector2 pos, Vector2 to)
{
    // とりあえず動かす用
    return new List<Common.MOVE_TYPE>();
}
// nowY > toY という前提で書いているので、それ以外はバグる
bool ExistsBlockX(int x, int nowY, int toY)
{
    return Enumerable.Range(nowY, toY).ToArray().All(y => map[y, x] == 0);
}
// 同じく
bool ExistsBlockY(int nowX, int toX, int y)
{
    return Enumerable.Range(nowX, toX).ToArray().All(x => map[y, x] == 0);
}

Common.MOVE_TYPE CoverEnemy(int i)
{
    var c = characterData[i].BlockPos;
    foreach (var e in enemyData)
    {
        var ePos = e.BlockPos;
        if (ePos.x == c.x)
        {
            if (ePos.y < c.y && ExistsBlockY(c.x, c.y, ePos.y)) return Common.MOVE_TYPE.DOWN;
            else if (ePos.y > c.y && ExistsBlockY(c.x, ePos.y, c.y)) return Common.MOVE_TYPE.UP;
        }
        else if (ePos.y == c.y)
        {
            if (ePos.x < c.x && ExistsBlockX(c.x, ePos.x, c.y)) return Common.MOVE_TYPE.RIGHT;
            else if (ePos.x > c.x && ExistsBlockX(ePos.x, c.x, c.y)) return Common.MOVE_TYPE.LEFT;
        }
    }
             
    return Common.MOVE_TYPE.NONE_MAX;
}
```

## initializeAI

```csharp:
mode = new MODE [] {MODE.PATROL, MODE.PATROL, MODE.PATROL};
map  = getStageData();

middleInterval = 30;
shortInterval = 180;
invincibleInterval = 900;

characterData = new List<CharacterModel.Data>();
enemyData = new List<CharacterModel.Data>();
DataUpdate();
```

## UpdateAI
```csharp=
DataUpdate();

for (var i = 0; i < 3; i++)
{
    var wayOfEnemy = CoverEnemy(i);
    
    switch(mode[i])
    {
    case MODE.PATROL:
        if (wayOfEnemy != Common.MOVE_TYPE.NONE_MAX)
        {
            mode[i] = MODE.ENCOUNT;
            Move(characterData[i].ActorId, wayOfEnemy);
        }
        else
        {
            Move(characterData[i].ActorId, RouteSearch(characterData[i].BlockPos).First());
        }
        break;
    case MODE.ENCOUNT:
        if (wayOfEnemy == Common.MOVE_TYPE.NONE_MAX)
        {
            mode[i] = MODE.PATROL;
            
            Action(chara.ActorId, Define.Battle.ACTION_TYPE.ATTACK_LONG);
        }
        break;
    case MODE.ITEM:
        mode[i] = MODE.PATROL; // あとでアイテムの処理を書く
        break;
    }
}
    
foreach (var chara in characterData)
    Action(chara.ActorId, Define.Battle.ACTION_TYPE.ATTACK_LONG);


```

## 状態更新(その他更新をかけたいものをまとめると楽かと)
```csharp=
        void DataUpdate()
        {
            // インターバルの更新
            middleInterval++;
            shortInterval++;
            invincibleInterval++;
            // 前回の状態を削除
            characterData.Clear();
            enemyData.Clear();
            // 自機の状態を取得            
            foreach (CharacterModel.Data data in GetTeamCharacterDataList(TEAM_TYPE.PLAYER))
            {
                characterData.Add(data);
            }
            // 敵機の状態を取得
            foreach (CharacterModel.Data data in GetTeamCharacterDataList(TEAM_TYPE.ENEMY))
            {
                enemyData.Add(data);
            }
        }
```

## 隣接ブロックへの移動可能判定
```csharp=
        // 入：座標(BlockPos)、出：そこから移動できる方向
        List<Common.MOVE_TYPE> RouteSearch(Vector2 position)
        {
            List<Common.MOVE_TYPE> movable = new List<Common.MOVE_TYPE>();
            int x = (int)position[0];
            int y = (int)position[1];

            if (map[y + 1, x] == 0)
            {
                movable.Add(Common.MOVE_TYPE.UP);
            }
            if (map[y - 1, x] == 0) {
                movable.Add(Common.MOVE_TYPE.DOWN);
            } 
            if (map[y, x - 1] == 0)
            {
                movable.Add(Common.MOVE_TYPE.LEFT);
            }
            if (map[y, x + 1] == 0)
            {
                movable.Add(Common.MOVE_TYPE.RIGHT);
            }

            return movable;
        }
```

## メイン機のキーボード操作

```csharp=
/* キーボードでメイン戦闘機を操作 */
/* 十字キーで移動、スペースキーで短距離攻撃 */
        override public void UpdateAI()
        {
            /* characterData[0].ActorIdはメイン自機*/
            /* 後で消す */
            if (Input.GetKey(KeyCode.UpArrow))
            {
                Move(characterData[0].ActorId, Common.MOVE_TYPE.UP);
            }
            if (Input.GetKey(KeyCode.DownArrow))
            {
                Move(characterData[0].ActorId, Common.MOVE_TYPE.DOWN);
            }
            if (Input.GetKey(KeyCode.LeftArrow))
            {
                Move(characterData[0].ActorId, Common.MOVE_TYPE.LEFT);
            }
            if (Input.GetKey(KeyCode.RightArrow))
            {
                Move(characterData[0].ActorId, Common.MOVE_TYPE.RIGHT);
            }
            if (Input.GetKeyDown(KeyCode.Space) && shortInterval > 180)
            {
                Action(characterData[0].ActorId, Define.Battle.ACTION_TYPE.ATTACK_SHORT);
                shortInterval = 0;
            }
        }
```


#### 現状
```csharp=
using UnityEngine;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

namespace Ateam
{
  public class Delta : BaseBattleAISystem
  {
    enum MODE
    {
      PATROL, ENCOUNT, ITEM, HELP
    }

    static int [,] map;
    MODE [] mode;

    List<CharacterModel.Data> characterData; // キャラクター情報のリスト
    List<CharacterModel.Data> enemyData; // 敵情報のリスト

    int shortInterval; // 短距離攻撃のインターバル
    int middleInterval; // 中距離攻撃のインターバル
    int invincibleInterval; // 無敵状態のインターバル

    private List<Common.MOVE_TYPE> AStar(Vector2 pos, Vector2 to)
    {
      return new List<Common.MOVE_TYPE>();
    }

    void DataUpdate()
    {
      characterData = new List<CharacterModel.Data>();
      enemyData     = new List<CharacterModel.Data>();
      // 自機の状態を取得            
      foreach (CharacterModel.Data data in GetTeamCharacterDataList(TEAM_TYPE.PLAYER))
      {
        characterData.Add(data);
      }
      // 敵機の状態を取得
      foreach (CharacterModel.Data data in GetTeamCharacterDataList(TEAM_TYPE.ENEMY))
      {
        enemyData.Add(data);
      }
    }

    // nowY > toY という前提で書いているので、それ以外はバグる
    bool NonExistsBlockX(float x, float nowY, float toY)
    {
      return Enumerable.Range((int)nowY, (int)toY).ToArray().Where(y => 0 <= y && y < map.GetLength(0)).All(y => map[y, (int)x] == 0);
    }

    // 同じく
    bool NonExistsBlockY(float nowX, float toX, float y)
    {
      return Enumerable.Range((int)nowX, (int)toX).ToArray().Where(x => 0<= x && x < map.GetLength(1)).All(x => map[(int)y, x] == 0);
    }

    Common.MOVE_TYPE CoverEnemy(int i)
    {
      var c = characterData[i].BlockPos;

      foreach (var e in enemyData)
      {
        var ePos = e.BlockPos;
        if (ePos.x == c.x)
        {
          if (ePos.y < c.y && NonExistsBlockY(c.x, c.y, ePos.y)) return Common.MOVE_TYPE.DOWN;
          else if (ePos.y > c.y && NonExistsBlockY(c.x, ePos.y, c.y)) return Common.MOVE_TYPE.UP;
        } 
        else if (ePos.y == c.y)
        {
          if (ePos.x < c.x && NonExistsBlockX(c.x, ePos.x, c.y)) return Common.MOVE_TYPE.LEFT;
          else if (ePos.x > c.x && NonExistsBlockX(ePos.x, c.x, c.y)) return Common.MOVE_TYPE.RIGHT;
        }
      }

      return Common.MOVE_TYPE.NONE_MAX;
    }

    List<Common.MOVE_TYPE> RouteSearch(Vector2 position)
    {
      List<Common.MOVE_TYPE> movable = new List<Common.MOVE_TYPE>();
      int x = (int)position[0];
      int y = (int)position[1];

      if (y+1 < map.GetLength(0) && map[y + 1, x] == 0)
      {
        movable.Add(Common.MOVE_TYPE.UP);
      }
      if (0 < y && map[y - 1, x] == 0) {
        movable.Add(Common.MOVE_TYPE.DOWN);
      } 
      if (0 < x && map[y, x - 1] == 0)
      {
        movable.Add(Common.MOVE_TYPE.LEFT);
      }
      if (x+1 < map.GetLength(1) && map[y, x + 1] == 0)
      {
        movable.Add(Common.MOVE_TYPE.RIGHT);
      }

      return movable;
    }

    //---------------------------------------------------
    // InitializeAI
    //---------------------------------------------------
    override public void InitializeAI()
    {
      mode =  new MODE [] {MODE.PATROL, MODE.PATROL, MODE.PATROL};
      map  = GetStageData();

      shortInterval = 0;
      middleInterval = 0;
      invincibleInterval = 0;

      characterData = new List<CharacterModel.Data>();
      enemyData     = new List<CharacterModel.Data>();
    }

    //---------------------------------------------------
    // UpdateAI
    //---------------------------------------------------
    override public void UpdateAI()
    {
      DataUpdate();

      for (var i = 0; i < 3; i++) {
        var wayOfEnemy = CoverEnemy(i);

        switch (mode[i])
        {
          case MODE.PATROL: 
            if (wayOfEnemy != Common.MOVE_TYPE.NONE_MAX)
            {
              Move(characterData[i].ActorId, wayOfEnemy);
              mode[i] = MODE.ENCOUNT;
            }
            else
            {
              var route = RouteSearch(characterData[i].BlockPos).ToArray();

              Move(characterData[i].ActorId, route[Random.Range(0, route.Length)]);
            }
            break;

          case MODE.ENCOUNT:
            if (wayOfEnemy == Common.MOVE_TYPE.NONE_MAX)
            {
              Action(characterData[i].ActorId, Define.Battle.ACTION_TYPE.ATTACK_SHORT);
              mode[i] = MODE.PATROL;
            }

            Move(characterData[i].ActorId, wayOfEnemy);
            Action(characterData[i].ActorId, Define.Battle.ACTION_TYPE.ATTACK_MIDDLE);
            Action(characterData[i].ActorId, Define.Battle.ACTION_TYPE.ATTACK_SHORT);
            Action(characterData[i].ActorId, Define.Battle.ACTION_TYPE.INVINCIBLE);
            break;

          case MODE.ITEM:
            mode[i] = MODE.PATROL; // あとでアイテムの処理を書く
            break;
      }

      Action(characterData[i].ActorId, Define.Battle.ACTION_TYPE.ATTACK_LONG);
    }
      
  }

    //---------------------------------------------------
    // ItemSpawnCallback
    //---------------------------------------------------
    override public void ItemSpawnCallback(ItemSpawnData itemData)
    {
      if (mode[1] == MODE.PATROL)
        mode[1] = MODE.ITEM;
      else
        mode[2] = MODE.ITEM;
    }
  }
}
```

#### 8/15追記分
GetByVecの辺りにバグがある。
仕様上一回消えかけたので、ここに一旦保存。

```csharp
using UnityEngine;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

namespace Ateam
{
  public class Delta : BaseBattleAISystem
  {
    enum MODE
    {
      PATROL, ENCOUNT, ITEM, WAIT
    }

    enum TO
    {
      ITEM, ENEMY
    }

    static int [,] map;
    MODE [] mode;

    List<CharacterModel.Data> characterData; // キャラクター情報のリスト
    List<CharacterModel.Data> enemyData; // 敵情報のリスト

    int target;

    Stack<Common.MOVE_TYPE> [] routesForItem  = new Stack<Common.MOVE_TYPE> [3];
    Stack<Common.MOVE_TYPE> [] routesForEnemy = new Stack<Common.MOVE_TYPE> [3];

    Vector2 DirToVec(Common.MOVE_TYPE direction)
    {
      switch (direction)
      {
        case Common.MOVE_TYPE.LEFT:  return Vector2.left;
        case Common.MOVE_TYPE.RIGHT: return Vector2.right;
        case Common.MOVE_TYPE.UP:    return Vector2.up;
        case Common.MOVE_TYPE.DOWN:  return Vector2.down;
      }

      return Vector2.zero;
    }

    T GetByVec<T>(T [,] m, Vector2 pos)
    {
      var y = pos.y < 0 ? 0 : (map.GetLength(0) <= pos.y ? map.GetLength(0) : (int)pos.y);
      var x = pos.x < 0 ? 0 : (map.GetLength(1) <= pos.x ? map.GetLength(1) : (int)pos.x);

      return m[y, x];
    }

    void SetByVec<T>(T [,] m, Vector2 pos, T t)
    {
      m[(int)pos.y, (int)pos.x] = t;
    }

    bool CanMove(int [,] m, Vector2 pos, Common.MOVE_TYPE direction)
    {
      var xMax = m.GetLength(1);
      var yMax = m.GetLength(0);

      switch (direction)
      {
        case Common.MOVE_TYPE.LEFT:  return 0 <= pos.y && pos.y < yMax && 0 <= pos.x-1   && GetByVec(m, pos + Vector2.left) == 0;
        case Common.MOVE_TYPE.RIGHT: return 0 <= pos.y && pos.y < yMax && pos.x+1 < xMax && GetByVec(m, pos + Vector2.right) == 0;
        case Common.MOVE_TYPE.UP:    return 0 <= pos.x && pos.x < xMax && pos.y+1 < yMax && GetByVec(m, pos + Vector2.up) == 0;
        case Common.MOVE_TYPE.DOWN:  return 0 <= pos.x && pos.x < xMax && 0 <= pos.y-1   && GetByVec(m, pos + Vector2.down) == 0;
      }

      return false;
    }

    void FindRoutesTo(TO to, int i, Vector2 goal)
    {
      var start   = characterData[i].BlockPos;
      var visitor = new Queue<Vector2>();
      var parent  = new Common.MOVE_TYPE [map.GetLength(0), map.GetLength(1)];

      visitor.Enqueue(start);

      var d = new int [map.GetLength(0), map.GetLength(1)];
      SetByVec(d, start, 1);

      while (visitor.Count != 0) {
        var now = visitor.Dequeue();
        
        if (now == goal) break;

        foreach (var move in new Common.MOVE_TYPE [] {Common.MOVE_TYPE.LEFT, Common.MOVE_TYPE.RIGHT, Common.MOVE_TYPE.UP, Common.MOVE_TYPE.DOWN})
          if (CanMove(d, now, move) && CanMove(map, now, move))
          {
            var p = now + DirToVec(move);
            SetByVec(d, p, 1); SetByVec(parent, p, move); visitor.Enqueue(p);
          }
      }

      var moveLog = GetByVec(parent, goal);
      var posLog  = goal - DirToVec(moveLog);

      while (posLog != start)
      {
        if (to == TO.ITEM) routesForItem[i].Push(moveLog);
        else routesForEnemy[i].Push(moveLog);

        moveLog = GetByVec(parent, posLog);
        posLog  = posLog - DirToVec(moveLog);
      }

      if (to == TO.ITEM) mode[i] = MODE.ITEM;
    }

    // nowY > toY という前提で書いているので、それ以外はバグる
    bool NonExistsBlockX(float x, float nowY, float toY)
    {
      return Enumerable.Range((int)nowY, (int)toY).ToArray().Where(y => 0 <= y && y < map.GetLength(0)).All(y => map[y, (int)x] == 0);
    }

    // 同じく
    bool NonExistsBlockY(float nowX, float toX, float y)
    {
      return Enumerable.Range((int)nowX, (int)toX).ToArray().Where(x => 0<= x && x < map.GetLength(1)).All(x => map[(int)y, x] == 0);
    }

    Common.MOVE_TYPE CoverEnemy(int i)
    {
      var c = characterData[i].BlockPos;

      foreach (var e in enemyData)
      {
        var ePos = e.BlockPos;
        if (ePos.x == c.x)
        {
          if (ePos.y < c.y && NonExistsBlockY(c.x, c.y, ePos.y)) return Common.MOVE_TYPE.DOWN;
          else if (ePos.y > c.y && NonExistsBlockY(c.x, ePos.y, c.y)) return Common.MOVE_TYPE.UP;
        } 
        else if (ePos.y == c.y)
        {
          if (ePos.x < c.x && NonExistsBlockX(c.x, ePos.x, c.y)) return Common.MOVE_TYPE.LEFT;
          else if (ePos.x > c.x && NonExistsBlockX(ePos.x, c.x, c.y)) return Common.MOVE_TYPE.RIGHT;
        }
      }

      return Common.MOVE_TYPE.NONE_MAX;
    }

    List<Common.MOVE_TYPE> RouteSearch(Vector2 position)
    {
      List<Common.MOVE_TYPE> movable = new List<Common.MOVE_TYPE>();
      int x = (int)position[0];
      int y = (int)position[1];

      if (y+1 < map.GetLength(0) && map[y + 1, x] == 0)
      {
        movable.Add(Common.MOVE_TYPE.UP);
      }
      if (0 < y && map[y - 1, x] == 0) {
        movable.Add(Common.MOVE_TYPE.DOWN);
      } 
      if (0 < x && map[y, x - 1] == 0)
      {
        movable.Add(Common.MOVE_TYPE.LEFT);
      }
      if (x+1 < map.GetLength(1) && map[y, x + 1] == 0)
      {
        movable.Add(Common.MOVE_TYPE.RIGHT);
      }

      return movable;
    }

    //---------------------------------------------------
    // InitializeAI
    //---------------------------------------------------
    override public void InitializeAI()
    {
      mode =  new MODE [] {MODE.PATROL, MODE.PATROL, MODE.PATROL};
      map  = GetStageData();

      target = 2;

      characterData = new List<CharacterModel.Data>();
      enemyData     = new List<CharacterModel.Data>();

      for (var i = 0; i < 3; i++) {
        routesForItem[i] = new Stack<Common.MOVE_TYPE>();
        routesForEnemy[i] = new Stack<Common.MOVE_TYPE>();
      }

      DataUpdate();
    }

    void DataUpdate()
    {
      // 前回の状態を削除
      characterData.Clear();
      enemyData.Clear();

      // 自機の状態を取得            
      foreach (CharacterModel.Data data in GetTeamCharacterDataList(TEAM_TYPE.PLAYER))
      {
        characterData.Add(data);
      }
      // 敵機の状態を取得
      foreach (CharacterModel.Data data in GetTeamCharacterDataList(TEAM_TYPE.ENEMY))
      {
        enemyData.Add(data);
      }

      if (enemyData[2].Hp <= 0) target = 1;
      if (enemyData[1].Hp <= 0) target = 0;
    }


    //---------------------------------------------------
    // UpdateAI
    //---------------------------------------------------
    override public void UpdateAI()
    {
      DataUpdate();

      for (var i = 0; i < 3; i++) {
        if (characterData[i].Hp == 0) continue;

        var directionOfEnemy = CoverEnemy(i);

        switch (mode[i])
        {
          case MODE.ITEM:
            if (routesForItem[i].Count > 0)
            {
              if (Move(characterData[i].ActorId, routesForItem[i].Peek()))
                routesForItem[i].Pop();
              Debug.Log(routesForItem[i].Count);
            }
            else mode[i] = MODE.ENCOUNT;
            break;

          case MODE.PATROL: 
            if (directionOfEnemy != Common.MOVE_TYPE.NONE_MAX)
            {
              routesForEnemy[i].Clear();
              if (!Move(characterData[i].ActorId, directionOfEnemy)) continue;
              if (!Action(characterData[i].ActorId, Define.Battle.ACTION_TYPE.INVINCIBLE)) continue;
              mode[i] = MODE.ENCOUNT;
            }
            else
            {
              if (routesForEnemy[i].Count > 0)
              {
                if (Move(characterData[i].ActorId, routesForEnemy[i].Peek()))
                    routesForEnemy[i].Pop();
              }
              else
              {
                var route = RouteSearch(characterData[i].BlockPos).ToArray();

                Move(characterData[i].ActorId, route[Random.Range(0, route.Length)]);
              }
            }
            break;

          case MODE.ENCOUNT:
            if (directionOfEnemy == Common.MOVE_TYPE.NONE_MAX) {
              FindRoutesTo(TO.ENEMY, i, enemyData[target].BlockPos);
              Action(characterData[i].ActorId, Define.Battle.ACTION_TYPE.ATTACK_SHORT);
              mode[i] = MODE.PATROL;
            }

            Move(characterData[i].ActorId, directionOfEnemy);
            Action(characterData[i].ActorId, Define.Battle.ACTION_TYPE.ATTACK_MIDDLE);
            break;
      }

      Action(characterData[i].ActorId, Define.Battle.ACTION_TYPE.ATTACK_LONG);
    }
      
  }

    //---------------------------------------------------
    // ItemSpawnCallback
    //---------------------------------------------------
    override public void ItemSpawnCallback(ItemSpawnData itemData)
    {
      DataUpdate();

      var i = characterData[1].Hp != 0 && mode[1] != MODE.ITEM ? 1 : 2;

      if (characterData[1].Hp == 0 && characterData[2].Hp == 0) i = 0;

      mode[i] = MODE.WAIT;
      FindRoutesTo(TO.ITEM, i, itemData.BlockPos);
    }
  }
}
```