Screeps:Arena Tutorialやった
                    
                Screeps:Arena Tutorialやった
- [[2022-04-08]] 買った
 
「JavaScript」を実際に打ち込んで対戦する『Screeps: Arena』が配信 をみて, 面白そうだと思ったので購入. [[CODINGAME]] みたいにゲームAIを書いて対戦するらしい.
チュートリアルのメモ書きです.
Tutorial
spawnCreep:creeps(ユニットのこと) を spawn- それぞれ名前といろいろなスキルを持つ
 Game.spawns['Spawn1']でアクセス
Game.spawns['Spawn1'].spawnCreep([WORK, CARRY, MOVE], 'Harvester1');
黄色いマスはエネルギー源, WORK で収穫でき, CARRY で運べる
Script
ctrl+enter で実行
creepは1500tickの寿命を持つ- この例では, 
spawnCreepには 200energy必要 
Room Controller
- 施設を色々作れる, レベル上げると色々出来る
 creep.memoryに色々記憶できる- 内容はタブで確認できる
 

import { getObjectsByPrototype } from "/game/utils";
import { Creep, Flag } from "/game/prototypes";
import {} from "/game/constants";
import {} from "/arena";
import { getTicks } from "/game/utils";
export function loop() {
  var myCreep = getObjectsByPrototype(Creep).find(creep => creep.my);
MOVE: cost = 50, 動けるATTACK: cost = 80, 攻撃できるRANGED_ATTACK: cost = 250, 3マス以内なら攻撃できるHEAL: cost = 250, 回復できるWORK: cost = 100, エネルギーを収穫できるCARRY: cost = 50, リソースを運べるTOUGH: cost = 10, 何も出来ない
const hasPart = (creep, part) => {
  return creep.body.some(bodyPart => bodyPart.type === part);
}
const invoke = (creep, method, ...args) => {
  creep[method](...args);
};
import { getObjectsByPrototype } from "/game/utils";
import { Creep } from "/game/prototypes";
import { ATTACK, RANGED_ATTACK, HEAL, ERR_NOT_IN_RANGE } from "/game/constants";
import {} from "/arena";
export function loop() {
  const party = getObjectsByPrototype(Creep).filter((c) => c.my);
  const enemies = getObjectsByPrototype(Creep).filter((c) => !c.my);
  for (let creep of party) {
    if (hasPart(creep, ATTACK)) {
      invoke(creep, "attack", enemies[0]);
    }
    if (hasPart(creep, RANGED_ATTACK)) {
      invoke(creep, "rangedAttack", enemies[0]);
    }
    if (hasPart(creep, HEAL)) {
      const damaged = party.find((c) => c.hits < c.hitsMax);
      if (damaged) {
        invoke(creep, "moveTo");
        invoke(creep, "heal", damaged);
      }
    }
  }
}
Towerは10エネルギー使って, 50マス以内の敵を攻撃する(減衰有り)creep.transfer(tower, RESOURCE_ENERGY)- エネルギーは 
Containerから引き出せるcreep.withdraw(container, RESOURCE_ENERGY)
 
const party = getObjectsByPrototype(Creep).filter((c) => c.my);
const enemies = getObjectsByPrototype(Creep).filter((c) => !c.my);
const tower = getObjectsByPrototype(StructureTower)[0];
const container = getObjectsByPrototype(StructureContainer)[0];
if (tower.store[RESOURCE_ENERGY] < 10) {
	if (party[0].store[RESOURCE_ENERGY] == 0) {
		invoke(party[0], "withdraw", container, RESOURCE_ENERGY);
	} else {
		invoke(party[0], "transfer", tower, RESOURCE_ENERGY);
	}
} else {
	invoke(tower, "attack", enemies[0]);
}
Terrain
Plain: 平地Natural: 壊せない壁Constructed: 壊せる壁Swamps: 5tickに1回しか動けない, 5個のMOVEがあれば毎ターン動けるRoads: ?? (後でちゃんと読む)
const flags = getObjectsByPrototype(Flag);
const flag = creep.findClosestByPath(flags);
creep.moveTo(flag);
spawnCreepに必要なRESOURCE_ENERGYが足りないと{ error: -6 }が返ってくる- 事前にコストを計算する必要がある
 
Havest Source
const source = getObjectsByPrototype(Source)[0];
creep.harvest(source);
Build Structure
const site = createConstructionSite(50, 55, StructureTower);
creep.build(site);
// -1: ERR_NOT_OWNER
sourceは.myがtrueにならないので注意
Tutorial: Final test
- しばらく経つと敵が壁を壊してこっちに来るのでそれまでにたくさんスポーンさせて迎え撃てばok
 - 色々ユーティリティコード書いたら膨れた
- フェーズ毎にコールバックを呼んでステートマシンみたいにした
 
 
import {
  getObjectsByPrototype,
  createConstructionSite,
  findClosestByPath,
} from "/game/utils";
import {
  // Flag,
  Creep,
  Source,
  StructureTower,
  StructureContainer,
  StructureSpawn,
  ConstructionSite,
} from "/game/prototypes";
import {
  MOVE,
  WORK,
  CARRY,
  ATTACK,
  RANGED_ATTACK,
  HEAL,
  ERR_NOT_IN_RANGE,
  RESOURCE_ENERGY,
} from "/game/constants";
import { getTicks } from "/game";
const hasPart = (creep, part) => {
  return creep.body.some((bodyPart) => bodyPart.type === part);
};
const invoke = (creep, method, ...args) => {
  const res = creep[method](...args);
  console.log(`${method}() => ${res}`);
  if (res === ERR_NOT_IN_RANGE) {
    creep.moveTo(args[0]);
  }
  return res;
};
const inspect = (creeps) => {
  console.log(
    creeps
      .filter((c) => !!c)
      .map((c) => c.body.map((part) => part.type).join(", "))
  );
};
const i = (view) => {
  console.log(`${view.name} @ (${view.x},${view.y})`);
};
const $ = (type, my = true) => {
  const result = getObjectsByPrototype(type).filter((o) =>
    my ? o.my === my : true
  )[0];
  if (!result) {
    console.log(`No ${type.name} found`);
    return null;
  }
  return result;
};
const $$ = (type, my = true) => {
  return getObjectsByPrototype(type).filter((o) => o.my === my);
};
const needCost = (parts) => {
  const map = {
    [MOVE]: 50,
    [WORK]: 100,
    [CARRY]: 50,
    [ATTACK]: 80,
    [RANGED_ATTACK]: 150,
    [HEAL]: 250,
  };
  return parts.map((part) => map[part]).reduce((a, b) => a + b, 0);
};
var party = [];
const tasks = [
  // spawn creeps
  () => {
    const spawner = $(StructureSpawn);
    const parts = [MOVE, ATTACK];
    if (party.length < 5 && spawner.store[RESOURCE_ENERGY] >= needCost(parts)) {
      const res = spawner.spawnCreep(parts);
      console.log(res);
      if (res.object) {
        party.push(res.object);
      }
    }
    console.log(
      `party: ${party.length}, spawner: ${spawner.store[RESOURCE_ENERGY]}`
    );
    // inspect(party);
    return party.length === 5;
  },
  // wait enemy
  () => {
    const enemies = getObjectsByPrototype(Creep).filter((c) => !c.my);
    inspect(enemies);
    const creep = $(Creep);
    const path = findClosestByPath(creep, enemies);
    console.log(`path: ${path}`);
    creep.moveTo(path);
    return !path;
  },
  // attack enemy
  () => {
    const creeps = $$(Creep);
    const enemies = getObjectsByPrototype(Creep).filter((c) => !c.my);
    creeps.forEach((creep) => {
      invoke(creep, "attack", enemies[0]);
    });
    return false;
  },
];
export function loop() {
  console.log(`t = ${getTicks()} tasks: ${tasks.length}`);
  const task = tasks[0];
  if (task && task()) {
    tasks.shift();
  }
}
所感
- import pathが通らないので補完が全く効かなく, DXが低い
- Rustで書きたい (Web Assembly として実行できるらしい? #todo )
 
 - デバッグが結構辛い
- エラーコードが定数なのでドキュメントを見に行かなければいけない
 - シミュレータみたいの欲しい