File size: 3,235 Bytes
022fcd2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/* ============================================================
   SELF-PLAY CLI — the Node entry point for tools/selfplay.js's pure self-play core. Split out (see
   docs/competitions-and-elo.md Phase 0) so tools/selfplay.js itself stays free of `process`/
   `document` and can be imported from a browser main thread or a Worker, not just Node. This file
   is Node-only by design — it's the thing that CAN'T be imported from a browser — so all of that
   lives here instead.

   Usage
     node tools/selfplay-cli.js run [--world ferros] [--seed 1] [--minutes 40]
                                    [--ai-strategy default] [--ai-difficulty medium]
                                    [--player-strategy default] [--player-difficulty medium]
   ============================================================ */

"use strict";

import { createSelfPlayState, runSelfPlayMatch } from "./selfplay.js";

function parseArgs(argv) {
  const out = { _: [] };
  for (let i = 0; i < argv.length; i++) {
    const a = argv[i];
    if (a.startsWith("--")) out[a.slice(2)] = (argv[i + 1] && !argv[i + 1].startsWith("--")) ? argv[++i] : "true";
    else out._.push(a);
  }
  return out;
}

function runCmd(args) {
  const state = createSelfPlayState({
    planetId: args.world || "ferros",
    seed: args.seed !== undefined ? Number(args.seed) : 1,
    matchTimeLimit: args.minutes !== undefined ? Number(args.minutes) * 60 : undefined,
    ai: { strategy: args["ai-strategy"], difficulty: args["ai-difficulty"] },
    playerAi: { strategy: args["player-strategy"], difficulty: args["player-difficulty"] },
  });
  const result = runSelfPlayMatch(state);
  const unitsOf = o => [...state.units.values()].filter(u => u.owner === o).length;
  const buildingsOf = o => [...state.buildings.values()].filter(b => b.owner === o).length;
  console.log(`world=${state.planetId} seed=${state.seed}`);
  console.log(`ai:     strategy=${state.ai.strategy} difficulty=${state.ai.difficulty}`);
  console.log(`player: strategy=${state.playerAi.strategy} difficulty=${state.playerAi.difficulty}`);
  console.log(`--`);
  console.log(`over=${result.over} winner=${result.winner} reason=${result.winReason} `
    + `time=${result.time.toFixed(1)}s tick=${result.tick}`);
  console.log(`ai:     units=${unitsOf("ai")} buildings=${buildingsOf("ai")} `
    + `resources=${JSON.stringify(state.players.ai.resources)}`);
  console.log(`player: units=${unitsOf("player")} buildings=${buildingsOf("player")} `
    + `resources=${JSON.stringify(state.players.player.resources)}`);
}

const USAGE = `SELF-PLAY — drive BOTH sides of a skirmish with the real AI (Tier 1).

  node tools/selfplay-cli.js run [--world ferros] [--seed 1] [--minutes 40]
                                 [--ai-strategy default] [--ai-difficulty medium]
                                 [--player-strategy default] [--player-difficulty medium]`;

function main(argv) {
  const args = parseArgs(argv);
  const cmd = args._[0];
  if (cmd === "run") { runCmd(args); return; }
  console.log(USAGE);
}

// Only run the CLI when invoked directly, so a test can import the functions above.
if (process.argv[1] && new URL(import.meta.url).pathname === process.argv[1]) main(process.argv.slice(2));