Classic, un exemple développé
Le joueur ordinateur intégré écrit en Lua : ses fichiers, la table des tâches, une tâche, les notes de bizarrerie, ce qui reste dans le moteur, et comment les mêmes ordres sont prouvés.
Ce chapitre résume docs/brains/oabrain-standard.md, docs/brains/authoring.md et docs/playing-with-brains.md. Ces documents sont le texte normatif.
Ce qu'est Classic
Classic est brains/classic. C'est la documentation du joueur ordinateur intégré : les mêmes ordres, écrits pour que vous puissiez les lire. Le joueur ordinateur intégré reste dans le moteur et reste le choix par défaut. Choisir Classic fait siéger ce paquet. Laisser le siège sur le joueur ordinateur intégré fait tourner le joueur ordinateur du moteur.
Le manifeste dit que works-with est une étoile, et que fullAwareness vaut true. Sa description dit qu'il joue exactement comme le joueur ordinateur intégré. La vue complète est le drapeau du manifeste. Elle est permise dans une partie en réseau, et le siège la signale. L'identifiant, le nom et le chemin d'installation ne font pas d'un paquet Classic. Les fichiers doivent être le dossier brains/classic de cette construction, les mêmes chemins et les mêmes octets.
Manifeste ·oabrain.yaml# SPDX-FileCopyrightText: The Open Annihilation Authors; see COPYRIGHT
# SPDX-License-Identifier: GPL-3.0-only
oabrain: 1
id: classic
name: Classic
description: "The engine's own computer player, written in Lua: it plays exactly as the built-in computer player does."
version: "1.0"
author:
name: The Open Annihilation Authors
tags: [classic, teaching]
works-with: ["*"]
requires:
engine: ">= 0.8.6"
levels:
- id: easy
name: Easy
difficulty: easy
- id: medium
name: Medium
difficulty: medium
- id: hard
name: Hard
difficulty: hard
fullAwareness: true
entry: brain.lua
Les fichiers
brain.lua construit une tâche pour chaque escouade de 0 à 9. Au premier tick, onTick rafraîchit les listes de construction et ensemence les grilles. Chaque tick exécute ensuite le tri des escouades sur son compte à rebours et exécute chaque tâche dont le tour est venu.
| Chemin | Rôle |
|---|---|
oabrain.yaml | Le manifeste. |
brain.lua | Le siège, la table des tâches et onTick. |
lua/build.lua | Note un type et en choisit un dans la liste d'un constructeur. |
lua/knowledge.lua | Poids, limites, grilles de construction et gisements de métal. Lit l'enregistrement du moteur dans me.knowledge. |
lua/place.lua | Cherche un site, en élargissant jusqu'à ce que la recherche soit aussi large que la carte. |
lua/profile.lua | Lit le texte du profil et applique les poids. |
lua/report.lua | Écrit le texte que report renvoie. |
lua/rules.lua | Lit les règles ai.* selon lesquelles Classic joue. |
lua/squads.lua | Range les unités en escouades et donne un ordre à une escouade. |
lua/types.lua | Lit les champs de type que le joueur ordinateur utilise. |
lua/tasks/ | structures, construction, strike, rally, air_raid et siege. |
README.md | Une note. Le jeu ne la charge pas. |
LICENSE | La licence. |
La table des tâches
Les escouades 1 et 5 construisent des structures. L'escouade 5 ne le fait que quand les règles le disent, et reste inactive sinon. Les escouades 2 et 6 sont des groupes de frappe. Les escouades 3 et 7 se rallient sur eux. L'escouade 4 est la construction. L'escouade 8 est l'escadre d'aéronefs. L'escouade 9 est siege. Le commentaire sur l'escouade 9 est une bizarrerie : le tri ne remplit jamais cette escouade, donc la tâche siege ne fait que garder son horaire, sauf si une unité y était déjà.
-- …
for index = 0, 9 do
seat.tasks[index] = blank(index)
end
local wave = loaded_rules.attack_wave
seat.tasks[1].kind = "structures"
seat.tasks[1].every = 30
seat.tasks[2].kind = "strike"
seat.tasks[2].min_size = 3
seat.tasks[2].launch = wave
seat.tasks[2].merge_radius = 20000
seat.tasks[2].source = 3
seat.tasks[2].every = 300
seat.tasks[3].kind = "rally"
seat.tasks[3].source = 2
seat.tasks[3].every = 150
seat.tasks[4].kind = "construction"
seat.tasks[4].every = 90
if loaded_rules.squad5_factory_tick then
seat.tasks[5].kind = "structures"
seat.tasks[5].every = 30
else
seat.tasks[5].kind = "idle"
end
seat.tasks[6].kind = "strike"
seat.tasks[6].min_size = 3
seat.tasks[6].launch = wave
seat.tasks[6].merge_radius = 50000
seat.tasks[6].source = 7
seat.tasks[6].every = 300
seat.tasks[7].kind = "rally"
seat.tasks[7].source = 6
seat.tasks[7].every = 150
seat.tasks[8].kind = "air_raid"
seat.tasks[8].every = function()
return 30 + brain.shared_random(900)
end
seat.tasks[8].run = function()
air_raid.run(seat, seat.tasks[8])
end
-- @quirk The sort never fills squad 9, so the siege task only keeps its
-- timetable unless a unit was already in that squad.
local centre = map_centre(game)
seat.tasks[9].kind = "siege"
seat.tasks[9].every = function()
return 30 + brain.shared_random(150)
end
seat.tasks[9].siege_target = point_of(centre)
seat.tasks[9].siege_probe = point_of(centre)
seat.tasks[9].siege_step = point_of(centre)
seat.tasks[9].siege_weight = 0
-- …
Au premier tick, onTick rafraîchit les listes de construction et ensemence les grilles. Le tri lui-même est le compte à rebours. Il commence à 30. Chaque tick en retire un, et en bas il revient à 30 et appelle le tri des escouades.
-- …
function hooks.onTick(tick)
local seat = hooks.seat
if seat.builds_ready ~= 1 then
types.refresh_builds(seat.types, seat.game)
seat.builds_ready = 1
end
if seat.grids_ready ~= 1 then
knowledge.seed_grids(seat)
end
seat.sort_countdown = seat.sort_countdown - 1
if seat.sort_countdown < 1 then
seat.sort_countdown = 30
brain.section("sort")
squads.sort(seat)
end
for index = 0, 9 do
local task = seat.tasks[index]
if task.kind ~= "none" and task.next <= tick then
if task.kind ~= "idle" then
brain.section("task" .. index)
local interval = task.every
if type(interval) == "function" then
interval = interval()
end
task.next = (tick + interval) & 0xFFFFFFFF
task.run()
end
end
end
end
-- …
squads.lua dit ce que fait le tri : les unités sélectionnables dans la portée du siège prennent des ordres permanents, et une unité encore dans l'escouade 0 rejoint l'escouade dans laquelle son type se range. Une unité déjà dans une escouade garde cette escouade.
Une tâche
lua/tasks/rally.lua est l'intégralité de la tâche rally. Quand l'escouade rally et le groupe strike ont tous deux des membres, l'escouade rally se déplace au centre du groupe strike.
-- SPDX-FileCopyrightText: The Open Annihilation Authors; see COPYRIGHT
-- SPDX-License-Identifier: GPL-3.0-only
-- A waiting squad. When it and the strike group both have members, it moves
-- to the strike group's centre. The timetable is already set.
local squads = require("squads")
local rally = {}
function rally.run(seat, task)
local me = seat.me
if squads.count(me, task.squad) < 1 or squads.count(me, task.source) < 1 then
return
end
local center = squads.centroid(me, task.source)
if center == nil then
return
end
squads.order(me, task.squad, "move", false, nil, squads.fixed_point(center))
end
return rally
Les notes de bizarrerie
Une ligne marquée @quirk enregistre un comportement du joueur ordinateur intégré. La note est dans le script pour qu'un lecteur la voie à côté du code. La note de l'escouade 9 est dans la table des tâches plus haut. Deux autres se trouvent dans profile.lua. Le mot any ne compte que comme premier mot après plan, et ce même mot est testé de nouveau sur chaque mot suivant de la ligne. Le ai_weight d'un type téléchargeable est appliqué une fois par joueur ordinateur, donc le poids se compose selon ce nombre.
-- The builders' tick. Each builder places a weighted pick near the base,
-- and an idle one patrols home. A capturing builder builds only while the
-- player is under the withhold count and the hold tick has passed.
-- @quirk ai.builder-withhold-threshold moves only the build limit: an idle
-- capturing builder still circles the base from five build-capable units.
La construction retient des constructions, et patrouille quand même. Une petite escadre d'aéronefs sans base, sous ai.patrol-null-enemy-skip, tire une patrouille au bord de la carte, les trois mêmes tirages qu'une grande escadre prend, et patrouille là au lieu de mettre en file une patrouille sur l'ennemi le plus proche.
Ce qui reste dans le moteur
Le joueur ordinateur intégré reste le choix par défaut. Un siège n'est Classic que quand ses fichiers correspondent au dossier de cette construction. Ce test accorde deux comportements, et un troisième tirage sur le flux de mise en place. brain.shared_random tire du flux partagé de la partie, celui dont tirent les scripts d'unités et la simulation, pour que les intervalles d'aéronefs et de siege de Classic restent au pas du joueur ordinateur intégré. unit:stance avec direct mis à true écrit les bits de posture de mouvement et de tir et ne donne pas d'ordre. Tout autre siège est refusé. brain.setup_random tire d'une copie de ce flux prise à la mise en place intégrée du siège, et n'avance que la copie.
Tout autre cerveau n'a aucune des deux fonctions aléatoires, et une écriture directe de stance est refusée. brain.section est installé pour chaque cerveau. Dans une partie, la marque ne fait rien. La vue complète n'est pas dans cette liste. C'est le drapeau du manifeste. Steady Commander n'en appelle aucune.
Comment il est prouvé
En shadow, Classic est mis en siège à côté du joueur ordinateur intégré. Les ordres du joueur ordinateur intégré sont ceux que la partie joue. Les ordres de Classic sont enregistrés et comparés. Aucun tick ne peut différer. Les tests du moteur pour cette comparaison sont sim-brains-shadow et sim-brains-shadow-match.
Mis à la place, Classic est mis en siège là où le joueur ordinateur intégré jouerait. Les tests du moteur pour cela sont sim-brains-classic, sim-brains-classic-data, sim-brains-classic-match, ai-computer-player, match-computer-build, native-trace, native-stage, native-director-render, native-classic-trace, native-classic-stage, native-classic-director-render, native-net-loopback-computer, native-net-loopback-computer-watcher, native-net-loopback-each-ai, native-net-loopback-computer-classic, native-net-loopback-computer-watcher-classic et native-net-loopback-each-ai-classic.
Un soak joue 1 000 escarmouches ensemencées et sans interface, de 20 000 ticks chacune, sur le jeu de base et les mods publiés. La même graine joue la même partie. Le soak est la longue vérification qu'un siège qui fait tourner Classic reste sur les ordres du joueur ordinateur intégré pendant toute une partie.
Comment le lire
Lisez Classic comme le comportement du joueur ordinateur intégré, mis par écrit. Le joueur ordinateur du moteur reste le joueur qu'un siège utilise quand personne n'a choisi de cerveau. Installer Classic, ou l'empaqueter, ne change rien tant qu'un siège n'est pas réglé dessus.