Dix scripts qui marchent
Les pages précédentes décrivent les fonctions une par une. Celle-ci donne des scripts entiers, à coller tels quels : chacun résout un problème que les workers ne savent pas résoudre seuls.
Ils sont vérifiés à chaque version — un test relit cette page, analyse chaque bloc et refuse toute fonction qui n'existerait plus. Ce que vous lisez ici démarre.
Changez les valeurs marquées // ← à vous avant de lancer quoi que ce soit : coordonnées, seuils, adresses.
fmt et strings s'importent, ils ne sont pas fournis : Sprintf et Contains n'existent pas tout seuls. C'est la faute la plus fréquente en recopiant un script, et l'erreur qu'elle donne — undefined symbol 'Sprintf' — ne dit pas qu'il manque un import.
1. La garde de nuit
Elle attend qu'une attaque arrive, prévient sur Discord, et se tait le reste du temps. Aucune requête au jeu tant qu'il ne se passe rien.
fmt = import("fmt")
SEUIL_SECONDES = 900 // ← à vous : alerter sous quinze minutes
LogInfo("garde de nuit en place")
for {
a, vu = WaitAttack(0) // 0 : attendre sans limite
if !vu {
break // le script s'arrête, on sort proprement
}
texte = fmt.Sprintf("%s attaque %s, impact dans %.0f min",
a.AttackerName, a.DestinationName, a.ArriveIn / 60)
LogWarn(texte)
SendDiscord(DISCORD_WEBHOOK, texte)
if a.ArriveIn < SEUIL_SECONDES {
SendTelegram(0, "URGENT : " + texte)
}
}
WaitAttack(0) rend deux valeurs : l'attaque, et un booléen qui dit si elle est venue. Le booléen passe à faux quand le script s'arrête — c'est la sortie de boucle.
2. Rentrer sa flotte à une heure imprévisible
Trois heures trente pile, toutes les nuits, se remarque. RangeCronExec tire l'heure au hasard dans un créneau.
fmt = import("fmt")
ORIGINE = "1:224:8" // ← à vous
ABRI = "1:224:9" // ← à vous : une lune ou une planète à vous
RangeCronExec("03:00:00", "03:45:00", "mise à l'abri", func() {
c = GetCachedCelestial(ORIGINE)
flotte, err = GetShips(c.GetID())
if err != nil {
LogError("flotte illisible :", err)
return
}
f = NewFleet()
f.SetOrigin(ORIGINE)
f.SetDestination(ABRI)
f.SetSpeed(TEN_PERCENT)
f.SetMission(PARK)
f.SetAllShips()
f.SetAllResources()
envoyée, err = f.SendNow()
if err != nil {
LogError("mise à l'abri refusée :", err)
SendDiscord(DISCORD_WEBHOOK, "la flotte n'est pas partie : " + fmt.Sprintf("%v", err))
return
}
LogInfo("flotte partie, retour à", envoyée.BackTime)
})
<-OnQuitCh // le script vit tant qu'on ne l'arrête pas
Sans <-OnQuitCh, le script se terminerait aussitôt et emporterait sa tâche avec lui.
3. Où envoyer pour rentrer après le réveil
Quand vous n'avez pas de lune où garer, il faut une destination assez lointaine pour que l'aller-retour couvre la nuit.
ORIGINE = "1:224:8" // ← à vous
HEURES = 7 // ← à vous : la durée de votre nuit
c = GetCachedCelestial(ORIGINE)
flotte, err = GetShips(c.GetID())
if err != nil {
LogError("flotte illisible :", err)
return
}
dest, secs, deut, err = FindDebrisFieldWithMinimumTravelTime(c.GetCoordinate(),
flotte, HEURES * 3600)
if err != nil {
LogWarn("aucun champ de ruines assez loin :", err)
dest, secs, deut, err = FindEmptyPlanetWithMinimumTravelTime(c.GetCoordinate(),
flotte, HEURES * 3600)
}
if err != nil {
LogError("rien d'assez loin, élargissez la zone du Scanner :", err)
return
}
Printf("%v : %.1f h de vol aller, %d deutérium", dest, secs / 3600, deut)
Ces fonctions ne coûtent aucune requête : elles lisent les relevés que le Scanner a déjà rapportés. Elles chiffrent à la vitesse la plus lente, celle qui tient le plus longtemps.
Notez le , et non . En anko, une division rend un flottant, même entre deux entiers : secs / 3600 vaut 8.5875 et non 8. Avec %d, la ligne sort %!d(float64=8.5875) — relevé en jouant cette recette sur un vrai compte. Voyez Boîte à outils.
4. Répondre à un message privé
Le canal du chat réveille le script à la seconde où quelqu'un écrit.
strings = import("strings")
for {
m, vu = WaitChatMessage(0)
if !vu {
break
}
if !m.Private() {
continue // le salon d'alliance ne nous regarde pas
}
LogInfo(m.SenderName + " écrit : " + m.Text)
if strings.Contains(strings.ToLower(m.Text), "gg") {
SendMessage(m.SenderID, "gg à toi")
} else {
SendDiscord(DISCORD_WEBHOOK, m.SenderName + " : " + m.Text)
}
}
Private() est la question qui décide de tout : un script qui répondrait à tout le salon d'alliance serait vite insupportable.
5. Guetter une cible et rappeler sa flotte
Les deux moments d'une surveillance, dans un seul script.
fmt = import("fmt")
JOUEUR = "Xylo" // ← à vous
err = AddHunterTarget(JOUEUR)
if err != nil {
LogError("mise sous surveillance impossible :", err)
return
}
StartHunter()
for {
e, vu = WaitHunterTargetActive(0)
if !vu {
break
}
texte = fmt.Sprintf("%s est revenu (avant : %d min)", e.PlayerName, e.PreviousActivity)
LogWarn(texte)
SendDiscord(DISCORD_WEBHOOK, texte)
}
Le réveil d'une cible pendant que votre flotte est dehors est exactement le moment de la rappeler. WaitHunterTargetIdle fait l'inverse : elle prévient quand la place se libère.
6. Le rapport du matin
Une fois par jour, ce que le compte a fait pendant la nuit.
fmt = import("fmt")
CronExec("@08h00", func() {
c = GetCachedCelestials()
an, mois, jour = Date()
lignes = fmt.Sprintf("rapport du %d/%d/%d\n", jour, mois, an)
total = 0
for i = 0; i < len(c); i++ {
res, err = GetResources(c[i].GetID())
if err != nil {
continue
}
total = total + res.Metal + res.Crystal + res.Deuterium
Sleep(500) // on n'enchaîne pas les lectures
}
colonies, possibles = CountColonies()
lignes = lignes + fmt.Sprintf("%d célestes, %d/%d colonies\n", len(c), colonies, possibles)
lignes = lignes + fmt.Sprintf("ressources en stock : %d\n", total)
rapports, err = GetEspionageReportMessages()
if err == nil {
lignes = lignes + fmt.Sprintf("%d rapports d'espionnage en boîte\n", len(rapports))
}
SendDiscord(DISCORD_WEBHOOK, lignes)
})
<-OnQuitCh
Sleep(500) entre deux lectures : le rythme des requêtes est le premier facteur de détection, et un rapport n'est jamais pressé.
7. Rendre la main au joueur le soir
Le pillage s'arrête à l'heure où vous jouez, et repart quand vous allez vous coucher.
DEBUT_SOIREE = "20:00:00" // ← à vous
FIN_SOIREE = "23:30:00" // ← à vous
CronExec(DEBUT_SOIREE, func() {
err = StopFarmingBot()
if err != nil {
LogError("le pillage n'a pas pu s'arrêter :", err)
return
}
LogInfo("pillage en pause, la soirée est à vous")
})
CronExec(FIN_SOIREE, func() {
err = StartFarmingBot()
if err != nil {
LogError("le pillage n'a pas pu repartir :", err)
return
}
LogInfo("pillage repris")
})
<-OnQuitCh
Allumer un outil éteint le reste. La phalange et la surveillance des champs de ruines sont exclusives : le robot n'en fait tourner qu'une, et l'allumer éteint les workers ordinaires. C'est la règle de l'interface, et elle s'applique aux scripts — un StartWorker("phalanx") arrêterait votre pillage sans que vous l'ayez demandé. Voyez Piloter les workers.
Les fonctions du pillage, elles, ne touchent qu'à lui.
8. Le chef d'orchestre
Un script tourne d'un bout à l'autre dans un seul fil : la garde de nuit et le rapport du matin ne peuvent pas vivre dans le même. Celui-ci n'agit pas sur le jeu — il allume et éteint les autres.
GARDE = "garde-de-nuit" // ← à vous : le nom de vos scripts
RAPPORT = "rapport"
CronExec("@22h00", func() {
if IsScriptRunning(GARDE) {
LogInfo("la garde tourne déjà")
return
}
err = StartScript(GARDE)
if err != nil { LogError("garde :", err) }
})
CronExec("@08h00", func() {
// « En pause » n'est pas « arrêté » : reprendre garde l'avancement,
// relancer repart de la première ligne.
if IsPausedScript(GARDE) {
ResumeScript(GARDE)
}
StopScript(GARDE)
StartScript(RAPPORT)
})
<-OnQuitCh
Le suffixe .ank est facultatif : "garde-de-nuit" et "garde-de-nuit.ank" désignent le même script.
Un script ne se met pas lui-même en pause, et c'est un refus explicite : plus personne ne tournerait pour le reprendre. Il peut en revanche s'arrêter, et c'est une sortie propre.
9. Rentrer pour huit heures, au tarif le plus bas
Une sauvegarde de flotte se pense par sa fin. Le calcul à l'envers cherche la vitesse la plus lente qui arrive encore à l'heure — la moins chère en deutérium parmi celles qu'on peut encore prendre.
fmt = import("fmt")
DEPART = "1:64:8" // ← à vous
ABRI = "1:64:9" // ← à vous
RETOUR = "08:00:00" // ← à vous : tout rentré à cette heure
flotte = NewShipsInfos()
flotte.Set(LARGECARGO, 20)
allure, deut, quand, err = LowestSpeed(RETOUR, DEPART, ABRI, flotte)
if err != nil {
LogError("aucune allure ne rentre à l'heure :", err)
return
}
LogInfo(fmt.Sprintf("départ à %s, %v%% de vitesse, %d deutérium",
quand.Format("15:04"), allure * 10, deut))
<-ExecAtCh(quand, func() {
// Un emplacement reste au Defender : c'est lui qui esquive.
slots = GetSlots()
if slots.Total - slots.InUse <= GetFleetSlotsReserved() {
LogWarn("plus de place, on laisse le Defender respirer")
return
}
f = NewFleet()
f.SetOrigin(DEPART)
f.SetDestination(ABRI)
f.SetMission(PARK)
f.SetSpeed(allure)
f.AddShips(LARGECARGO, 20)
f.SetAllResources()
vol, err = f.SendNow()
if err != nil {
LogError("envoi :", err)
return
}
LogInfo("flotte partie, vol", vol.ID)
})
LowestSpeed coûte six requêtes : une pour la borne, puis cinq au plus pour chercher par dichotomie parmi les allures du compte (vingt pour un Général, dix pour les autres). À appeler une fois, au moment de planifier, jamais dans une boucle sur cent cibles.
Un décollage exactement maintenant ne compte pas : le temps de composer la flotte et de cliquer, la seconde est passée, et c'est l'allure au-dessus qui est retenue.
10. La moisson du matin
Les chapitres distribuent des ressources contre des tâches déjà accomplies. Il n'y a aucune raison de ne pas les ramasser, sinon qu'il faut y penser tous les jours.
CHAPITRE = 4006 // ← à vous : lu dans l'adresse de la page
BASE = "1:64:8" // ← à vous
CronExec("@09h00", func() {
err = ChapterClaimAll(CHAPITRE)
if err != nil {
LogWarn("chapitre :", err) // rien à ramasser n'est pas une panne
}
// Puis on rassemble : la base reçoit, tout le reste l'alimente.
err = RepatriateSetAllDestinations(BASE)
if err != nil {
LogError("rapatriement :", err)
return
}
err = RepatriateNow()
if err != nil { LogError("rapatriement :", err) }
})
<-OnQuitCh
RepatriateSetAllDestinations remplace la liste des origines, elle ne la complète pas : une colonie que vous aviez mise de côté y revient. Lisez le nom jusqu'au bout.
Le worker doit être allumé pour que RepatriateNow fasse quelque chose : déclencher un worker éteint ne ferait rien, et l'erreur le dit.
Ce qu'ils ont en commun
Le contrôle du retour. Chaque appel qui peut échouer est testé. Un script qui ignore ses erreurs tourne en apparence et ne fait rien — c'est le défaut le plus coûteux, parce que rien ne le signale.
<-OnQuitCh à la fin de tout script qui planifie. Sans lui, le script se termine et ses tâches meurent avec lui.
Le rythme. Sleep entre deux lectures qui touchent le jeu. Les fonctions de cache — GetCachedCelestials, GetCachedPlayer — n'en ont pas besoin : elles ne demandent rien.
Les webhooks sont ceux de vos réglages. DISCORD_WEBHOOK et SendTelegram(0, …) prennent ce que vous avez rempli dans la page Notifications ; rien de secret ne traîne dans vos fichiers.