Classic, un esempio svolto
Il giocatore computer integrato scritto in Lua: i suoi file, la tabella dei compiti, un compito, le note sulle particolarità, che cosa resta nel motore e come si dimostrano gli stessi ordini.
Questo capitolo riassume docs/brains/oabrain-standard.md, docs/brains/authoring.md e docs/playing-with-brains.md. Quei documenti sono il testo normativo.
Che cos'è Classic
Classic è brains/classic. È la documentazione del giocatore computer integrato: gli stessi ordini, scritti perché tu possa leggerli. Il giocatore computer integrato resta nel motore e resta il predefinito. Scegliere Classic mette al posto questo pacchetto. Lasciare il posto sul giocatore computer integrato esegue il giocatore computer proprio del motore.
Il manifesto dice che works-with è un asterisco, e che fullAwareness è true. La sua descrizione dice che gioca esattamente come il giocatore computer integrato. La vista completa è il flag del manifesto. È ammessa in una partita in rete, e il posto la segnala. L'identificativo, il nome e il percorso di installazione non rendono Classic un pacchetto. I file devono essere la cartella brains/classic di questa build, gli stessi percorsi e gli stessi byte.
Manifesto ·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
I file
brain.lua costruisce un compito per ogni squadra da 0 a 9. Al primo tick onTick aggiorna le liste di costruzione e semina le griglie. Ogni tick esegue poi l'ordinamento delle squadre sul suo conto alla rovescia, ed esegue ogni compito a cui è arrivato il turno.
| Percorso | Cosa fa |
|---|---|
oabrain.yaml | Il manifesto. |
brain.lua | Il posto, la tabella dei compiti e onTick. |
lua/build.lua | Dà un punteggio a un tipo e ne sceglie uno dalla lista di un costruttore. |
lua/knowledge.lua | Pesi, limiti, le griglie di costruzione e i punti di metallo. Legge la scheda del motore da me.knowledge. |
lua/place.lua | Cerca un sito, e allarga la ricerca finché è larga quanto la mappa. |
lua/profile.lua | Legge il testo del profilo e applica i pesi. |
lua/report.lua | Scrive il testo che restituisce report. |
lua/rules.lua | Legge le regole ai.* con cui gioca Classic. |
lua/squads.lua | Smista le unità in squadre e dà un ordine a una squadra. |
lua/types.lua | Legge i campi del tipo che usa il giocatore computer. |
lua/tasks/ | structures, construction, strike, rally, air_raid e siege. |
README.md | Una nota. Il gioco non la carica. |
LICENSE | La licenza. |
La tabella dei compiti
Le squadre 1 e 5 costruiscono strutture. La squadra 5 lo fa solo quando lo dicono le regole, e altrimenti resta inattiva. Le squadre 2 e 6 sono gruppi d'assalto. Le squadre 3 e 7 si radunano su di essi. La squadra 4 è costruzione. La squadra 8 è l'ala aerei. La squadra 9 è assedio. Il commento sulla squadra 9 è una particolarità: l'ordinamento non riempie mai quella squadra, quindi il compito di assedio tiene solo il suo orario, salvo che ci fosse già un'unità.
-- …
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
-- …
Al primo tick onTick aggiorna le liste di costruzione e semina le griglie. L'ordinamento stesso è il conto alla rovescia. Parte da 30. Ogni tick sottrae uno, e in fondo torna a 30 e chiama l'ordinamento delle squadre.
-- …
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 dice che cosa fa l'ordinamento: le unità selezionabili nel raggio del posto prendono gli ordini permanenti, e un'unità ancora nella squadra 0 entra nella squadra in cui la smista il suo tipo. Un'unità che è già in una squadra tiene quella squadra.
Un compito
lua/tasks/rally.lua è l'intero compito rally. Quando la squadra rally e il gruppo strike hanno entrambi delle unità, la squadra rally si sposta al centro del gruppo 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
Le note sulle particolarità
Una riga marcata @quirk registra un comportamento del giocatore computer integrato. La nota sta nello script così chi legge la vede accanto al codice. La nota sulla squadra 9 è nella tabella dei compiti sopra. Altre due stanno in profile.lua. La parola any conta solo come prima parola dopo plan, e quella stessa parola viene riprovata su ogni parola successiva della riga. L'ai_weight di un tipo scaricabile si applica una volta per giocatore computer, quindi il peso si compone per quel conteggio.
-- 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 costruzione trattiene le costruzioni, e pattuglia lo stesso. Un'ala aerei piccola e senza base, sotto ai.patrol-null-enemy-skip, estrae un pattugliamento sul bordo della mappa, le stesse tre estrazioni che fa un'ala grande, e pattuglia lì invece di accodare un pattugliamento sul nemico più vicino.
Che cosa resta nel motore
Il giocatore computer integrato resta il predefinito. Un posto è Classic solo quando i suoi file coincidono con la cartella di questa build. Quel test concede due comportamenti, e una terza estrazione dal flusso di preparazione. brain.shared_random estrae il flusso condiviso della partita, quello da cui estraggono gli script delle unità e la simulazione, così gli intervalli degli aerei e dell'assedio di Classic restano al passo con il giocatore computer integrato. unit:stance con direct impostato a true scrive i bit permanenti di movimento e di fuoco e non dà un ordine. Qualsiasi altro posto è rifiutato. brain.setup_random estrae da una copia di quel flusso presa alla preparazione integrata del posto, e fa avanzare solo la copia.
Nessun altro cervello ha nessuna delle due funzioni casuali, e una scrittura diretta di stance è rifiutata. brain.section è installato per ogni cervello. In una partita il segno non fa nulla. La vista completa non è in questo elenco. È il flag del manifesto. Steady Commander non ne chiama nessuno.
Come si dimostra
In ombra, Classic viene messo al posto accanto al giocatore computer integrato. Gli ordini del giocatore computer integrato sono quelli che gioca la partita. Quelli di Classic vengono registrati e confrontati. Nessun tick può differire. I test del motore per quel confronto sono sim-brains-shadow e sim-brains-shadow-match.
Messo al suo posto, Classic viene messo dove giocherebbe il giocatore computer integrato. I test del motore per questo sono 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 e native-net-loopback-each-ai-classic.
Un soak gioca 1.000 schermaglie headless con seme, di 20.000 tick ciascuna, sul gioco base e sulle mod pubblicate. Lo stesso seme gioca la stessa partita. Il soak è il controllo lungo che un posto che esegue Classic resti sugli ordini del giocatore computer integrato per un'intera partita.
Come leggerlo
Leggi Classic come il comportamento del giocatore computer integrato, messo per iscritto. Il giocatore computer proprio del motore è ancora il giocatore che un posto usa quando nessuno ha scelto un cervello. Installare Classic, o impacchettarlo, non cambia nulla finché un posto non è impostato su di esso.