diff --git a/dist/client.d.ts b/dist/client.d.ts index 2296db5..4d7485c 100644 --- a/dist/client.d.ts +++ b/dist/client.d.ts @@ -1,9 +1,14 @@ import { Point } from "./pb/physics_pb.js"; -import { OrderSet } from "./pb/server_pb.js"; -import { Bot } from './stub.js'; +import { Order, OrderSet } from "./pb/server_pb.js"; import { EnvVarLoader } from './configurator.js'; +import GameSnapshotInspector from "./game-snapshot-inspector.js"; +import { Bot } from './stub.js'; export declare const PROTOCOL_VERSION = "1.0.0"; -export type RawTurnProcessor = (OrderSet: any, GameSnapshot: any) => Promise; +export type RawTurnProcessorReturn = Order[] | { + orders: Order[]; + debug_message: string; +} | null; +export type RawTurnProcessor = (inspector: GameSnapshotInspector) => Promise; /** * * @param {EnvVarLoader} config @@ -23,7 +28,7 @@ export declare class Client { private readonly init_position; private client; /** - * @type {function(GameSnapshot)} + * @type {function(GameSnapshotInspector)} */ private gettingReadyHandler; /** @@ -52,11 +57,11 @@ export declare class Client { play(raw_processor: RawTurnProcessor, onJoin?: () => void): Promise; /** * - * @param {function(GameSnapshot)} handler + * @param {function(GameSnapshotInspector)} handler * * @returns {Client} */ - setGettingReadyHandler(handler: any): this; + setGettingReadyHandler(handler: (s: GameSnapshotInspector) => void): this; /** * * @param {function(OrderSet, GameSnapshot):OrderSet} processor diff --git a/dist/client.js b/dist/client.js index 05e8071..15319a8 100644 --- a/dist/client.js +++ b/dist/client.js @@ -37,11 +37,12 @@ var __generator = (this && this.__generator) || function (thisArg, body) { }; exports.__esModule = true; exports.Client = exports.NewClientFromConfig = exports.PROTOCOL_VERSION = void 0; -var server_pb_js_1 = require("./pb/server_pb.js"); -var server_grpc_pb_1 = require("./pb/server_grpc_pb"); var grpc_js_1 = require("@grpc/grpc-js"); +var server_grpc_pb_1 = require("./pb/server_grpc_pb"); +var server_pb_js_1 = require("./pb/server_pb.js"); +var game_snapshot_inspector_js_1 = require("./game-snapshot-inspector.js"); +var index_1 = require("./index"); var stub_js_1 = require("./stub.js"); -var index_js_1 = require("./index.js"); exports.PROTOCOL_VERSION = "1.0.0"; /** * @@ -66,7 +67,7 @@ var Client = /** @class */ (function () { */ function Client(server_add, grpc_insecure, token, teamSide, number, init_position) { /** - * @type {function(GameSnapshot)} + * @type {function(GameSnapshotInspector)} */ this.gettingReadyHandler = function (gs) { }; @@ -91,25 +92,25 @@ var Client = /** @class */ (function () { return __generator(this, function (_a) { return [2 /*return*/, this.setGettingReadyHandler(function (s) { bot.gettingReady(s); - })._start(function (ordersSet, snapshot) { + })._start(function (inspector) { return new Promise(function (resolve, reject) { - var playerState = (0, index_js_1.defineState)(snapshot, _this.number, _this.teamSide); + var playerState = (0, index_1.defineState)(inspector, _this.number, _this.teamSide); if (_this.number === 1) { - resolve(bot.asGoalkeeper(ordersSet, snapshot, playerState)); + resolve(bot.asGoalkeeper(inspector, playerState)); return; } switch (playerState) { case stub_js_1.PLAYER_STATE.DISPUTING_THE_BALL: - resolve(bot.onDisputing(ordersSet, snapshot)); + resolve(bot.onDisputing(inspector)); break; case stub_js_1.PLAYER_STATE.DEFENDING: - resolve(bot.onDefending(ordersSet, snapshot)); + resolve(bot.onDefending(inspector)); break; case stub_js_1.PLAYER_STATE.SUPPORTING: - resolve(bot.onSupporting(ordersSet, snapshot)); + resolve(bot.onSupporting(inspector)); break; case stub_js_1.PLAYER_STATE.HOLDING_THE_BALL: - resolve(bot.onHolding(ordersSet, snapshot)); + resolve(bot.onHolding(inspector)); break; } }); @@ -133,7 +134,7 @@ var Client = /** @class */ (function () { }; /** * - * @param {function(GameSnapshot)} handler + * @param {function(GameSnapshotInspector)} handler * * @returns {Client} */ @@ -173,11 +174,12 @@ var Client = /** @class */ (function () { var running = _this.client.joinATeam(req); onJoin(); running.on('data', function (snapshot) { return __awaiter(_this, void 0, void 0, function () { - var _a, orderSet, e_1, e_2; + var inspector, _a, orderSet, botReturn, e_1, e_2; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 11, , 12]); + inspector = new game_snapshot_inspector_js_1["default"](this.teamSide, this.number, snapshot); _a = snapshot.getState(); switch (_a) { case server_pb_js_1.GameSnapshot.State.LISTENING: return [3 /*break*/, 1]; @@ -190,9 +192,19 @@ var Client = /** @class */ (function () { _b.label = 2; case 2: _b.trys.push([2, 4, , 5]); - return [4 /*yield*/, processor(orderSet, snapshot)]; + return [4 /*yield*/, processor(inspector)]; case 3: - orderSet = _b.sent(); + botReturn = _b.sent(); + if (!botReturn) { + orderSet.setOrdersList([]); + } + if (Array.isArray(botReturn)) { + orderSet.setOrdersList(botReturn); + } + else { + orderSet.setOrdersList(botReturn.orders); + orderSet.setDebugMessage(botReturn.debug_message); + } return [3 /*break*/, 5]; case 4: e_1 = _b.sent(); @@ -209,7 +221,7 @@ var Client = /** @class */ (function () { _b.label = 8; case 8: return [3 /*break*/, 10]; case 9: - this.gettingReadyHandler(snapshot); + this.gettingReadyHandler(inspector); return [3 /*break*/, 10]; case 10: return [3 /*break*/, 12]; case 11: diff --git a/dist/game-snapshot-inspector.d.ts b/dist/game-snapshot-inspector.d.ts new file mode 100644 index 0000000..1d338f2 --- /dev/null +++ b/dist/game-snapshot-inspector.d.ts @@ -0,0 +1,36 @@ +import { DIRECTION } from '.'; +import * as Lugo from './proto_exported'; +export default class GameSnapshotInspector { + mySide: Lugo.Team.Side; + myNumber: number; + me: Lugo.Player; + snapshot: Lugo.GameSnapshot; + constructor(botSide: Lugo.Team.Side, playerNumber: number, gameSnapshot: Lugo.GameSnapshot); + getSnapshot(): Lugo.GameSnapshot | null; + getTurn(): number; + getMe(): Lugo.Player; + getBall(): Lugo.Ball | null; + getPlayer(side: Lugo.Team.Side, number: number): Lugo.Player | null; + getBallHolder(): Lugo.Player | null; + isBallHolder(player: Lugo.Player): boolean; + getTeam(side: Lugo.Team.Side): Lugo.Team | null; + getMyTeam(): Lugo.Team | null; + getOpponentTeam(): Lugo.Team | null; + getMyTeamSide(): Lugo.Team.Side; + getOpponentSide(): Lugo.Team.Side; + getMyTeamPlayers(): Lugo.Player[]; + getOpponentPlayers(): Lugo.Player[]; + getMyTeamGoalkeeper(): Lugo.Player | null; + getOpponentGoalkeeper(): Lugo.Player | null; + makeOrderMove(target: Lugo.Point, speed: number): Lugo.Order; + makeOrderMoveMaxSpeed(target: Lugo.Point): Lugo.Order; + makeOrderMoveFromPoint(origin: Lugo.Point, target: Lugo.Point, speed: number): Lugo.Order; + makeOrderMoveFromVector(direction: Lugo.Vector, speed: number): Lugo.Order; + makeOrderMoveByDirection(direction: DIRECTION, speed?: number): Lugo.Order; + makeOrderMoveToStop(): Lugo.Order; + makeOrderJump(target: Lugo.Point, speed: number): Lugo.Order; + makeOrderKick(target: Lugo.Point, speed: number): Lugo.Order; + makeOrderKickMaxSpeed(target: Lugo.Point): Lugo.Order; + makeOrderCatch(): Lugo.Order; + private getOrientationByDirection; +} diff --git a/dist/game-snapshot-inspector.js b/dist/game-snapshot-inspector.js new file mode 100644 index 0000000..de3ba6f --- /dev/null +++ b/dist/game-snapshot-inspector.js @@ -0,0 +1,184 @@ +"use strict"; +exports.__esModule = true; +var _1 = require("."); +var Geo = require("./geo"); +var Helpers = require("./helpers"); +var ORIENTATION = require("./orentation"); +var Lugo = require("./proto_exported"); +var specs_1 = require("./specs"); +var GameSnapshotInspector = /** @class */ (function () { + function GameSnapshotInspector(botSide, playerNumber, gameSnapshot) { + this.mySide = botSide; + this.myNumber = playerNumber; + this.snapshot = gameSnapshot; + this.me = this.getPlayer(botSide, playerNumber); + if (!this.me) { + throw new Error("Could not find the player ".concat(botSide, "-").concat(playerNumber)); + } + } + GameSnapshotInspector.prototype.getSnapshot = function () { + return this.snapshot; + }; + GameSnapshotInspector.prototype.getTurn = function () { + return this.snapshot.getTurn(); + }; + GameSnapshotInspector.prototype.getMe = function () { + return this.me; + }; + GameSnapshotInspector.prototype.getBall = function () { + var _a, _b; + return (_b = (_a = this.snapshot) === null || _a === void 0 ? void 0 : _a.getBall()) !== null && _b !== void 0 ? _b : null; + }; + GameSnapshotInspector.prototype.getPlayer = function (side, number) { + return Helpers.getPlayer(this.snapshot, side, number); + }; + GameSnapshotInspector.prototype.getBallHolder = function () { + return Helpers.getBallHolder(this.snapshot); + }; + GameSnapshotInspector.prototype.isBallHolder = function (player) { + return Helpers.isBallHolder(this.snapshot, player); + }; + GameSnapshotInspector.prototype.getTeam = function (side) { + return Helpers.getTeam(this.snapshot, side); + }; + GameSnapshotInspector.prototype.getMyTeam = function () { + return this.getTeam(this.mySide); + }; + GameSnapshotInspector.prototype.getOpponentTeam = function () { + return this.getTeam(this.getOpponentSide()); + }; + GameSnapshotInspector.prototype.getMyTeamSide = function () { + return this.mySide; + }; + GameSnapshotInspector.prototype.getOpponentSide = function () { + return Helpers.getOpponentSide(this.mySide); + }; + GameSnapshotInspector.prototype.getMyTeamPlayers = function () { + var myTeam = this.getMyTeam(); + return myTeam ? myTeam.getPlayersList() : []; + }; + GameSnapshotInspector.prototype.getOpponentPlayers = function () { + var opponentTeam = this.getOpponentTeam(); + return opponentTeam ? opponentTeam.getPlayersList() : []; + }; + GameSnapshotInspector.prototype.getMyTeamGoalkeeper = function () { + return this.getPlayer(this.getMyTeamSide(), specs_1.SPECS.GOALKEEPER_NUMBER); + }; + GameSnapshotInspector.prototype.getOpponentGoalkeeper = function () { + return this.getPlayer(this.getOpponentSide(), specs_1.SPECS.GOALKEEPER_NUMBER); + }; + GameSnapshotInspector.prototype.makeOrderMove = function (target, speed) { + var _a, _b; + return this.makeOrderMoveFromPoint((_b = (_a = this.me) === null || _a === void 0 ? void 0 : _a.getPosition()) !== null && _b !== void 0 ? _b : Geo.newZeroedPoint(), target, speed); + }; + GameSnapshotInspector.prototype.makeOrderMoveMaxSpeed = function (target) { + var _a, _b; + return this.makeOrderMoveFromPoint((_b = (_a = this.me) === null || _a === void 0 ? void 0 : _a.getPosition()) !== null && _b !== void 0 ? _b : Geo.newZeroedPoint(), target, specs_1.SPECS.PLAYER_MAX_SPEED); + }; + GameSnapshotInspector.prototype.makeOrderMoveFromPoint = function (origin, target, speed) { + var vec = Geo.NewVector(origin, target); + var vel = Geo.NewZeroedVelocity(Geo.normalize(vec)); + vel.setSpeed(speed); + var moveOrder = new Lugo.Move(); + moveOrder.setVelocity(vel); + return new Lugo.Order().setMove(moveOrder); + }; + GameSnapshotInspector.prototype.makeOrderMoveFromVector = function (direction, speed) { + var _a, _b, _c, _d; + var targetPoint = Geo.TargetFrom(direction, (_b = (_a = this.me) === null || _a === void 0 ? void 0 : _a.getPosition()) !== null && _b !== void 0 ? _b : Geo.newZeroedPoint()); + return this.makeOrderMoveFromPoint((_d = (_c = this.me) === null || _c === void 0 ? void 0 : _c.getPosition()) !== null && _d !== void 0 ? _d : Geo.newZeroedPoint(), targetPoint, speed); + }; + GameSnapshotInspector.prototype.makeOrderMoveByDirection = function (direction, speed) { + var directionTarget = this.getOrientationByDirection(direction); + return this.makeOrderMoveFromVector(directionTarget, speed !== null && speed !== void 0 ? speed : specs_1.SPECS.PLAYER_MAX_SPEED); + }; + GameSnapshotInspector.prototype.makeOrderMoveToStop = function () { + var _a, _b, _c; + var myDirection = (_c = (_b = (_a = this.getMe()) === null || _a === void 0 ? void 0 : _a.getVelocity()) === null || _b === void 0 ? void 0 : _b.getDirection()) !== null && _c !== void 0 ? _c : this.getOrientationByDirection(_1.DIRECTION.FORWARD); + return this.makeOrderMoveFromVector(myDirection, 0); + }; + GameSnapshotInspector.prototype.makeOrderJump = function (target, speed) { + var _a, _b; + var vec = Geo.NewVector((_b = (_a = this.me) === null || _a === void 0 ? void 0 : _a.getPosition()) !== null && _b !== void 0 ? _b : Geo.newZeroedPoint(), target); + var vel = Geo.NewZeroedVelocity(Geo.normalize(vec)); + vel.setSpeed(speed); + var jump = new Lugo.Jump(); + jump.setVelocity(vel); + return new Lugo.Order().setJump(jump); + }; + GameSnapshotInspector.prototype.makeOrderKick = function (target, speed) { + var _a, _b, _c, _d, _e, _f, _g; + var ballExpectedDirection = Geo.NewVector((_c = (_b = (_a = this.snapshot) === null || _a === void 0 ? void 0 : _a.getBall()) === null || _b === void 0 ? void 0 : _b.getPosition()) !== null && _c !== void 0 ? _c : Geo.newZeroedPoint(), target); + var diffVector = Geo.subVector(ballExpectedDirection, (_g = (_f = (_e = (_d = this.snapshot) === null || _d === void 0 ? void 0 : _d.getBall()) === null || _e === void 0 ? void 0 : _e.getVelocity()) === null || _f === void 0 ? void 0 : _f.getDirection()) !== null && _g !== void 0 ? _g : Geo.newZeroedPoint()); + var vel = Geo.NewZeroedVelocity(Geo.normalize(diffVector)); + vel.setSpeed(speed); + var kick = new Lugo.Kick(); + kick.setVelocity(vel); + return new Lugo.Order().setKick(kick); + }; + GameSnapshotInspector.prototype.makeOrderKickMaxSpeed = function (target) { + return this.makeOrderKick(target, specs_1.SPECS.BALL_MAX_SPEED); + }; + GameSnapshotInspector.prototype.makeOrderCatch = function () { + var catchOrder = new Lugo.Catch(); + return new Lugo.Order().setCatch(catchOrder); + }; + GameSnapshotInspector.prototype.getOrientationByDirection = function (direction) { + var directionTarget; + switch (direction) { + case _1.DIRECTION.FORWARD: + directionTarget = ORIENTATION.EAST; + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.WEST; + } + break; + case _1.DIRECTION.BACKWARD: + directionTarget = ORIENTATION.WEST; + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.EAST; + } + break; + case _1.DIRECTION.LEFT: + directionTarget = ORIENTATION.NORTH; + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.SOUTH; + } + break; + case _1.DIRECTION.RIGHT: + directionTarget = ORIENTATION.SOUTH; + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.NORTH; + } + break; + case _1.DIRECTION.BACKWARD_LEFT: + directionTarget = ORIENTATION.NORTH_WEST; + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.SOUTH_EAST; + } + break; + case _1.DIRECTION.BACKWARD_RIGHT: + directionTarget = ORIENTATION.SOUTH_WEST; + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.NORTH_EAST; + } + break; + case _1.DIRECTION.FORWARD_LEFT: + directionTarget = ORIENTATION.NORTH_EAST; + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.SOUTH_WEST; + } + break; + case _1.DIRECTION.FORWARD_RIGHT: + directionTarget = ORIENTATION.SOUTH_EAST; + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.NORTH_WEST; + } + break; + default: + throw new Error("unknown direction ".concat(direction)); + } + return directionTarget; + }; + return GameSnapshotInspector; +}()); +exports["default"] = GameSnapshotInspector; diff --git a/dist/geo.d.ts b/dist/geo.d.ts index a999aca..9986f6b 100644 --- a/dist/geo.d.ts +++ b/dist/geo.d.ts @@ -1,4 +1,4 @@ -import { Point, Vector } from './pb/physics_pb.js'; +import { Point, Vector, Velocity } from './pb/physics_pb.js'; /** * * @param {Point} from @@ -33,3 +33,6 @@ export declare function getScaledVector(v: Vector, scale: number): Vector; */ export declare function subVector(originalV: Vector, subV: Vector): Vector; export declare function distanceBetweenPoints(a: Point, b: Point): number; +export declare function NewZeroedVelocity(direction: Vector): Velocity; +export declare function TargetFrom(v: Vector, point: Point): Point; +export declare function newZeroedPoint(): Point; diff --git a/dist/geo.js b/dist/geo.js index 61e4262..fd5f8a6 100644 --- a/dist/geo.js +++ b/dist/geo.js @@ -1,6 +1,6 @@ "use strict"; exports.__esModule = true; -exports.distanceBetweenPoints = exports.subVector = exports.getScaledVector = exports.getLength = exports.normalize = exports.NewVector = void 0; +exports.newZeroedPoint = exports.TargetFrom = exports.NewZeroedVelocity = exports.distanceBetweenPoints = exports.subVector = exports.getScaledVector = exports.getLength = exports.normalize = exports.NewVector = void 0; var physics_pb_js_1 = require("./pb/physics_pb.js"); /** * @@ -82,3 +82,21 @@ function distanceBetweenPoints(a, b) { return Math.hypot(a.getX() - b.getX(), a.getY() - b.getY()); } exports.distanceBetweenPoints = distanceBetweenPoints; +function NewZeroedVelocity(direction) { + var velocity = new physics_pb_js_1.Velocity(); + velocity.setDirection(direction); + velocity.setSpeed(0); + return velocity; +} +exports.NewZeroedVelocity = NewZeroedVelocity; +function TargetFrom(v, point) { + var target = new physics_pb_js_1.Point(); + target.setX(point.getX() + Math.round(v.getX())); + target.setX(point.getY() + Math.round(v.getY())); + return target; +} +exports.TargetFrom = TargetFrom; +function newZeroedPoint() { + return new physics_pb_js_1.Point().setX(0).setY(0); +} +exports.newZeroedPoint = newZeroedPoint; diff --git a/dist/goal.d.ts b/dist/goal.d.ts index 388036c..8e464e5 100644 --- a/dist/goal.d.ts +++ b/dist/goal.d.ts @@ -18,3 +18,5 @@ export declare class Goal { getTopPole(): Point; getBottomPole(): Point; } +export declare const AWAY_GOAL: Goal; +export declare const HOME_GOAL: Goal; diff --git a/dist/goal.js b/dist/goal.js index 956bb19..332305b 100644 --- a/dist/goal.js +++ b/dist/goal.js @@ -1,6 +1,27 @@ "use strict"; exports.__esModule = true; -exports.Goal = void 0; +exports.HOME_GOAL = exports.AWAY_GOAL = exports.Goal = void 0; +var physics_pb_1 = require("./pb/physics_pb"); +var server_pb_1 = require("./pb/server_pb"); +var specs_1 = require("./specs"); +var homeGoalCenter = new physics_pb_1.Point(); +homeGoalCenter.setX(0); +homeGoalCenter.setY(specs_1.SPECS.MAX_Y_COORDINATE / 2); +var homeGoalTopPole = new physics_pb_1.Point(); +homeGoalTopPole.setX(0); +homeGoalTopPole.setY(specs_1.SPECS.GOAL_MAX_Y); +var homeGoalBottomPole = new physics_pb_1.Point(); +homeGoalBottomPole.setX(0); +homeGoalBottomPole.setY(specs_1.SPECS.GOAL_MIN_Y); +var awayGoalCenter = new physics_pb_1.Point(); +awayGoalCenter.setX(specs_1.SPECS.MAX_X_COORDINATE); +awayGoalCenter.setY(specs_1.SPECS.MAX_Y_COORDINATE / 2); +var awayGoalTopPole = new physics_pb_1.Point(); +awayGoalTopPole.setX(specs_1.SPECS.MAX_X_COORDINATE); +awayGoalTopPole.setY(specs_1.SPECS.GOAL_MAX_Y); +var awayGoalBottomPole = new physics_pb_1.Point(); +awayGoalBottomPole.setX(specs_1.SPECS.MAX_X_COORDINATE); +awayGoalBottomPole.setY(specs_1.SPECS.GOAL_MIN_Y); var Goal = /** @class */ (function () { /** * @@ -30,3 +51,5 @@ var Goal = /** @class */ (function () { return Goal; }()); exports.Goal = Goal; +exports.AWAY_GOAL = new Goal(server_pb_1.Team.Side.AWAY, awayGoalCenter, awayGoalTopPole, awayGoalBottomPole); +exports.HOME_GOAL = new Goal(server_pb_1.Team.Side.HOME, homeGoalCenter, homeGoalTopPole, homeGoalBottomPole); diff --git a/dist/helpers.d.ts b/dist/helpers.d.ts new file mode 100644 index 0000000..bc21bbd --- /dev/null +++ b/dist/helpers.d.ts @@ -0,0 +1,6 @@ +import { GameSnapshot, Player, Team } from "./proto_exported"; +export declare function getBallHolder(snapshot: GameSnapshot): Player | null; +export declare function isBallHolder(snapshot: GameSnapshot, player: Player): boolean; +export declare function getTeam(snapshot: GameSnapshot, side: Team.Side): Team | null; +export declare function getPlayer(snapshot: GameSnapshot, side: Team.Side, number: number): Player | null; +export declare function getOpponentSide(side: Team.Side): Team.Side; diff --git a/dist/helpers.js b/dist/helpers.js new file mode 100644 index 0000000..1ce98c0 --- /dev/null +++ b/dist/helpers.js @@ -0,0 +1,40 @@ +"use strict"; +exports.__esModule = true; +exports.getOpponentSide = exports.getPlayer = exports.getTeam = exports.isBallHolder = exports.getBallHolder = void 0; +var proto_exported_1 = require("./proto_exported"); +function getBallHolder(snapshot) { + var holder = snapshot.getBall().getHolder(); + return holder !== null && holder !== void 0 ? holder : null; +} +exports.getBallHolder = getBallHolder; +function isBallHolder(snapshot, player) { + var holder = snapshot.getBall().getHolder(); + return holder !== undefined && holder.getTeamSide() === player.getTeamSide() && holder.getNumber() === player.getNumber(); +} +exports.isBallHolder = isBallHolder; +function getTeam(snapshot, side) { + var _a, _b; + if (side === proto_exported_1.Team.Side.HOME) { + return (_a = snapshot.getHomeTeam()) !== null && _a !== void 0 ? _a : null; + } + return (_b = snapshot.getAwayTeam()) !== null && _b !== void 0 ? _b : null; +} +exports.getTeam = getTeam; +function getPlayer(snapshot, side, number) { + var team = getTeam(snapshot, side); + if (team) { + var player = null; + for (var _i = 0, _a = team.getPlayersList(); _i < _a.length; _i++) { + var currentPlayer = _a[_i]; + if (currentPlayer.getNumber() === number) + player = currentPlayer; + } + return player; + } + return null; +} +exports.getPlayer = getPlayer; +function getOpponentSide(side) { + return side === proto_exported_1.Team.Side.HOME ? proto_exported_1.Team.Side.AWAY : proto_exported_1.Team.Side.HOME; +} +exports.getOpponentSide = getOpponentSide; diff --git a/dist/index.d.ts b/dist/index.d.ts index 13aa661..fc0e838 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1,119 +1,19 @@ import { Client, NewClientFromConfig } from './client'; import { EnvVarLoader } from './configurator'; +import GameSnapshotInspector from './game-snapshot-inspector'; +import * as geo from "./geo"; +import { NewVector, distanceBetweenPoints, getLength, getScaledVector, normalize, subVector } from "./geo"; import { Goal } from './goal'; import { Mapper, Region } from './mapper'; import * as ORIENTATION from './orentation'; import * as Lugo from './proto_exported'; +import * as rl from "./rl/index"; import { SPECS } from "./specs.js"; import { Bot, PLAYER_STATE } from './stub'; -import * as geo from "./geo"; -import { normalize, distanceBetweenPoints, getLength, subVector, getScaledVector, NewVector } from "./geo"; -import * as rl from "./rl/index"; -export { rl, Client, NewClientFromConfig, EnvVarLoader, Goal, Mapper, Region, ORIENTATION, SPECS, Bot, PLAYER_STATE, Lugo, geo, // keeping backward compatibility -normalize, distanceBetweenPoints, getLength, subVector, getScaledVector, NewVector, }; -export declare class GameSnapshotReader { - readonly mySide: any; - /** - * @type {Lugo.GameSnapshot} - */ - readonly snapshot: any; - constructor(snapshot: Lugo.GameSnapshot, mySide: Lugo.Team.Side); - /** - * Returns the bot team - * @returns {Lugo.Team} - */ - getMyTeam(): Lugo.Team; - /** - * Returns the opponent team - * @returns {Lugo.Team} - */ - getOpponentTeam(): Lugo.Team; - /** - * @param { Lugo.Team.Side} side - * @returns {Lugo.Team} - */ - getTeam(side: any): Lugo.Team; - /** - * - * @param { Player} player - * @returns {boolean} - */ - isBallHolder(player: Lugo.Player): boolean; - /** - * - * @returns {Lugo.Team.Side} - */ - getOpponentSide(): Lugo.Team.Side; - /** - * - * @returns {Goal} - */ - getMyGoal(): Goal; - /** - * - * @returns {Lugo.Ball} - */ - getBall(): Lugo.Ball; - /** - * - * @returns {Goal} - */ - getOpponentGoal(): Goal; - /** - * - * @param {.Lugo.Team.Side} side - * @param {number} number - * @returns {.Player} - */ - getPlayer(side: Lugo.Team.Side, number: number): Lugo.Player | null; - /** - * - * @param {Point} origin - * @param {Point} target - * @return {Order} - */ - makeOrderMoveMaxSpeed(origin: Lugo.Point, target: Lugo.Point): Lugo.Order; - /** - * - * @param {Point} origin - * @param {Point} target - * @param speed - * @returns {Order} - */ - makeOrderMove(origin: Lugo.Point, target: Lugo.Point, speed: number): Lugo.Order; - /** - * - * @param {Vector} direction - * @param {number} speed - * @returns {Order} - * @private - */ - makeOrderMoveFromVector(direction: Lugo.Vector, speed: number): Lugo.Order; - makeOrderMoveByDirection(direction: DIRECTION): Lugo.Order; - makeOrderJump(origin: Lugo.Point, target: Lugo.Point, speed: number): Lugo.Order; - /** - * - * @param {Ball} ball - * @param {Point} target - * @param {number} speed - * @returns {Order} - */ - makeOrderKick(ball: Lugo.Ball, target: Lugo.Point, speed: number): Lugo.Order; - /** - * - * @param {Ball} ball - * @param {Point} target - * @returns {Order} - */ - makeOrderKickMaxSpeed(ball: Lugo.Ball, target: Lugo.Point): Lugo.Order; - /** - * - * @returns {!Order} - */ - makeOrderCatch(): Lugo.Order; -} -export declare const awayGoal: Goal; -export declare const homeGoal: Goal; +import { NewDefaultStarter } from './starter'; +import { DefaultInitBundle } from './util/defaults'; +export { Bot, Client, EnvVarLoader, GameSnapshotInspector, Goal, Lugo, Mapper, NewClientFromConfig, NewVector, ORIENTATION, PLAYER_STATE, Region, SPECS, distanceBetweenPoints, geo, getLength, getScaledVector, // keeping backward compatibility +normalize, rl, subVector, NewDefaultStarter, DefaultInitBundle }; export declare enum DIRECTION { FORWARD = 0, BACKWARD = 1, @@ -126,9 +26,9 @@ export declare enum DIRECTION { } /** * - * @param {GameSnapshot} snapshot + * @param {GameSnapshotInspector} snapshot * @param playerNumber * @param side * @returns {PLAYER_STATE} */ -export declare function defineState(snapshot: Lugo.GameSnapshot, playerNumber: number, side: Lugo.Team.Side): PLAYER_STATE; +export declare function defineState(snapshot: GameSnapshotInspector, playerNumber: number, side: Lugo.Team.Side): PLAYER_STATE; diff --git a/dist/index.js b/dist/index.js index 88dabe2..d9c97aa 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,11 +1,22 @@ "use strict"; exports.__esModule = true; -exports.defineState = exports.DIRECTION = exports.homeGoal = exports.awayGoal = exports.GameSnapshotReader = exports.NewVector = exports.getScaledVector = exports.subVector = exports.getLength = exports.distanceBetweenPoints = exports.normalize = exports.geo = exports.Lugo = exports.PLAYER_STATE = exports.SPECS = exports.ORIENTATION = exports.Region = exports.Mapper = exports.Goal = exports.EnvVarLoader = exports.NewClientFromConfig = exports.Client = exports.rl = void 0; +exports.defineState = exports.DIRECTION = exports.DefaultInitBundle = exports.NewDefaultStarter = exports.subVector = exports.rl = exports.normalize = exports.getScaledVector = exports.getLength = exports.geo = exports.distanceBetweenPoints = exports.SPECS = exports.Region = exports.PLAYER_STATE = exports.ORIENTATION = exports.NewVector = exports.NewClientFromConfig = exports.Mapper = exports.Lugo = exports.Goal = exports.GameSnapshotInspector = exports.EnvVarLoader = exports.Client = void 0; var client_1 = require("./client"); exports.Client = client_1.Client; exports.NewClientFromConfig = client_1.NewClientFromConfig; var configurator_1 = require("./configurator"); exports.EnvVarLoader = configurator_1.EnvVarLoader; +var game_snapshot_inspector_1 = require("./game-snapshot-inspector"); +exports.GameSnapshotInspector = game_snapshot_inspector_1["default"]; +var geo = require("./geo"); +exports.geo = geo; +var geo_1 = require("./geo"); +exports.NewVector = geo_1.NewVector; +exports.distanceBetweenPoints = geo_1.distanceBetweenPoints; +exports.getLength = geo_1.getLength; +exports.getScaledVector = geo_1.getScaledVector; +exports.normalize = geo_1.normalize; +exports.subVector = geo_1.subVector; var goal_1 = require("./goal"); exports.Goal = goal_1.Goal; var mapper_1 = require("./mapper"); @@ -15,280 +26,16 @@ var ORIENTATION = require("./orentation"); exports.ORIENTATION = ORIENTATION; var Lugo = require("./proto_exported"); exports.Lugo = Lugo; +var rl = require("./rl/index"); +exports.rl = rl; var specs_js_1 = require("./specs.js"); exports.SPECS = specs_js_1.SPECS; var stub_1 = require("./stub"); exports.PLAYER_STATE = stub_1.PLAYER_STATE; -var geo = require("./geo"); -exports.geo = geo; -var geo_1 = require("./geo"); -exports.normalize = geo_1.normalize; -exports.distanceBetweenPoints = geo_1.distanceBetweenPoints; -exports.getLength = geo_1.getLength; -exports.subVector = geo_1.subVector; -exports.getScaledVector = geo_1.getScaledVector; -exports.NewVector = geo_1.NewVector; -var rl = require("./rl/index"); -exports.rl = rl; -var homeGoalCenter = new Lugo.Point(); -homeGoalCenter.setX(0); -homeGoalCenter.setY(specs_js_1.SPECS.MAX_Y_COORDINATE / 2); -var homeGoalTopPole = new Lugo.Point(); -homeGoalTopPole.setX(0); -homeGoalTopPole.setY(specs_js_1.SPECS.GOAL_MAX_Y); -var homeGoalBottomPole = new Lugo.Point(); -homeGoalBottomPole.setX(0); -homeGoalBottomPole.setY(specs_js_1.SPECS.GOAL_MIN_Y); -var awayGoalCenter = new Lugo.Point(); -awayGoalCenter.setX(specs_js_1.SPECS.MAX_X_COORDINATE); -awayGoalCenter.setY(specs_js_1.SPECS.MAX_Y_COORDINATE / 2); -var awayGoalTopPole = new Lugo.Point(); -awayGoalTopPole.setX(specs_js_1.SPECS.MAX_X_COORDINATE); -awayGoalTopPole.setY(specs_js_1.SPECS.GOAL_MAX_Y); -var awayGoalBottomPole = new Lugo.Point(); -awayGoalBottomPole.setX(specs_js_1.SPECS.MAX_X_COORDINATE); -awayGoalBottomPole.setY(specs_js_1.SPECS.GOAL_MIN_Y); -var GameSnapshotReader = /** @class */ (function () { - function GameSnapshotReader(snapshot, mySide) { - this.snapshot = snapshot; - this.mySide = mySide; - } - /** - * Returns the bot team - * @returns {Lugo.Team} - */ - GameSnapshotReader.prototype.getMyTeam = function () { - return this.getTeam(this.mySide); - }; - /** - * Returns the opponent team - * @returns {Lugo.Team} - */ - GameSnapshotReader.prototype.getOpponentTeam = function () { - return this.getTeam(this.getOpponentSide()); - }; - /** - * @param { Lugo.Team.Side} side - * @returns {Lugo.Team} - */ - GameSnapshotReader.prototype.getTeam = function (side) { - if (side === Lugo.Side.HOME) { - return this.snapshot.getHomeTeam(); - } - return this.snapshot.getAwayTeam(); - }; - /** - * - * @param { Player} player - * @returns {boolean} - */ - GameSnapshotReader.prototype.isBallHolder = function (player) { - var ball = this.snapshot.getBall(); - return ball.getHolder() != null && ball.getHolder().getTeamSide() === player.getTeamSide() && ball.getHolder().getNumber() === player.getNumber(); - }; - /** - * - * @returns {Lugo.Team.Side} - */ - GameSnapshotReader.prototype.getOpponentSide = function () { - if (this.mySide === Lugo.Team.Side.HOME) { - return Lugo.Team.Side.AWAY; - } - return Lugo.Team.Side.HOME; - }; - /** - * - * @returns {Goal} - */ - GameSnapshotReader.prototype.getMyGoal = function () { - if (this.mySide === Lugo.Team.Side.HOME) { - return exports.homeGoal; - } - return exports.awayGoal; - }; - /** - * - * @returns {Lugo.Ball} - */ - GameSnapshotReader.prototype.getBall = function () { - return this.snapshot.getBall(); - }; - /** - * - * @returns {Goal} - */ - GameSnapshotReader.prototype.getOpponentGoal = function () { - if (this.mySide === Lugo.Team.Side.HOME) { - return exports.awayGoal; - } - return exports.homeGoal; - }; - /** - * - * @param {.Lugo.Team.Side} side - * @param {number} number - * @returns {.Player} - */ - GameSnapshotReader.prototype.getPlayer = function (side, number) { - var team = this.getTeam(side); - if (team == null) { - return null; - } - for (var _i = 0, _a = team.getPlayersList(); _i < _a.length; _i++) { - var player = _a[_i]; - if (player.getNumber() === number) { - return player; - } - } - return null; - }; - /** - * - * @param {Point} origin - * @param {Point} target - * @return {Order} - */ - GameSnapshotReader.prototype.makeOrderMoveMaxSpeed = function (origin, target) { - return this.makeOrderMove(origin, target, specs_js_1.SPECS.PLAYER_MAX_SPEED); - }; - /** - * - * @param {Point} origin - * @param {Point} target - * @param speed - * @returns {Order} - */ - GameSnapshotReader.prototype.makeOrderMove = function (origin, target, speed) { - if (origin.getX() === target.getX() && origin.getY() === target.getY()) { - // a vector cannot have zeroed direction. In this case, the player will just be stopped - return this.makeOrderMoveFromVector(ORIENTATION.NORTH, 0); - } - var direction = geo.NewVector(origin, target); - direction = geo.normalize(direction); - return this.makeOrderMoveFromVector(direction, speed); - }; - /** - * - * @param {Vector} direction - * @param {number} speed - * @returns {Order} - * @private - */ - GameSnapshotReader.prototype.makeOrderMoveFromVector = function (direction, speed) { - var velocity = new Lugo.Velocity(); - velocity.setDirection(direction); - velocity.setSpeed(speed); - var moveOrder = new Lugo.Move(); - moveOrder.setVelocity(velocity); - return new Lugo.Order().setMove(moveOrder); - }; - GameSnapshotReader.prototype.makeOrderMoveByDirection = function (direction) { - var directionTarget; - switch (direction) { - case DIRECTION.FORWARD: - directionTarget = ORIENTATION.EAST; - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.WEST; - } - break; - case DIRECTION.BACKWARD: - directionTarget = ORIENTATION.WEST; - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.EAST; - } - break; - case DIRECTION.LEFT: - directionTarget = ORIENTATION.NORTH; - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.SOUTH; - } - break; - case DIRECTION.RIGHT: - directionTarget = ORIENTATION.SOUTH; - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.NORTH; - } - break; - case DIRECTION.BACKWARD_LEFT: - directionTarget = ORIENTATION.NORTH_WEST; - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.SOUTH_EAST; - } - break; - case DIRECTION.BACKWARD_RIGHT: - directionTarget = ORIENTATION.SOUTH_WEST; - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.NORTH_EAST; - } - break; - case DIRECTION.FORWARD_LEFT: - directionTarget = ORIENTATION.NORTH_EAST; - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.SOUTH_WEST; - } - break; - case DIRECTION.FORWARD_RIGHT: - directionTarget = ORIENTATION.SOUTH_EAST; - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.NORTH_WEST; - } - break; - default: - throw new Error("unknown direction ".concat(direction)); - } - return this.makeOrderMoveFromVector(directionTarget, specs_js_1.SPECS.PLAYER_MAX_SPEED); - }; - GameSnapshotReader.prototype.makeOrderJump = function (origin, target, speed) { - var direction = ORIENTATION.EAST; - if (origin.getX() !== target.getX() || origin.getY() !== target.getY()) { - // a vector cannot have zeroed direction. In this case, the player will just be stopped - direction = geo.NewVector(origin, target); - direction = geo.normalize(direction); - } - var velocity = new Lugo.Velocity(); - velocity.setDirection(direction); - velocity.setSpeed(speed); - var jump = new Lugo.Jump(); - jump.setVelocity(velocity); - return new Lugo.Order().setJump(jump); - }; - /** - * - * @param {Ball} ball - * @param {Point} target - * @param {number} speed - * @returns {Order} - */ - GameSnapshotReader.prototype.makeOrderKick = function (ball, target, speed) { - var ballExpectedDirection = geo.NewVector(ball.getPosition(), target); - // the ball velocity is summed to the kick velocity, so we have to consider the current ball direction - var diffVector = geo.subVector(ballExpectedDirection, ball.getVelocity().getDirection()); - var newVelocity = new Lugo.Velocity(); - newVelocity.setSpeed(speed); - newVelocity.setDirection(geo.normalize(diffVector)); - return new Lugo.Order().setKick(new Lugo.Kick().setVelocity(newVelocity)); - }; - /** - * - * @param {Ball} ball - * @param {Point} target - * @returns {Order} - */ - GameSnapshotReader.prototype.makeOrderKickMaxSpeed = function (ball, target) { - return this.makeOrderKick(ball, target, specs_js_1.SPECS.BALL_MAX_SPEED); - }; - /** - * - * @returns {!Order} - */ - GameSnapshotReader.prototype.makeOrderCatch = function () { - return new Lugo.Order().setCatch(new Lugo.Catch()); - }; - return GameSnapshotReader; -}()); -exports.GameSnapshotReader = GameSnapshotReader; -exports.awayGoal = new goal_1.Goal(Lugo.Team.Side.AWAY, awayGoalCenter, awayGoalTopPole, awayGoalBottomPole); -exports.homeGoal = new goal_1.Goal(Lugo.Team.Side.HOME, homeGoalCenter, homeGoalTopPole, homeGoalBottomPole); +var starter_1 = require("./starter"); +exports.NewDefaultStarter = starter_1.NewDefaultStarter; +var defaults_1 = require("./util/defaults"); +exports.DefaultInitBundle = defaults_1.DefaultInitBundle; var DIRECTION; (function (DIRECTION) { DIRECTION[DIRECTION["FORWARD"] = 0] = "FORWARD"; @@ -302,7 +49,7 @@ var DIRECTION; })(DIRECTION = exports.DIRECTION || (exports.DIRECTION = {})); /** * - * @param {GameSnapshot} snapshot + * @param {GameSnapshotInspector} snapshot * @param playerNumber * @param side * @returns {PLAYER_STATE} @@ -311,8 +58,7 @@ function defineState(snapshot, playerNumber, side) { if (!snapshot || !snapshot.getBall()) { throw new Error('invalid snapshot state - cannot define player state'); } - var reader = new GameSnapshotReader(snapshot, side); - var me = reader.getPlayer(side, playerNumber); + var me = snapshot.getPlayer(side, playerNumber); if (!me) { throw new Error('could not find the bot in the snapshot - cannot define player state'); } diff --git a/dist/mapper.d.ts b/dist/mapper.d.ts index 7c22418..9b1c541 100644 --- a/dist/mapper.d.ts +++ b/dist/mapper.d.ts @@ -1,3 +1,4 @@ +import { Goal } from './goal.js'; import * as physics from './pb/physics_pb.js'; import * as lugo from './pb/server_pb.js'; export declare class Region { @@ -99,6 +100,8 @@ export declare class Mapper { * @param side {lugo.Team.Side} */ constructor(cols: number, rows: number, side: lugo.Team.Side); + getDefenseGoal(): Goal; + getAttackGoal(): Goal; /** * @param col {number} * @param row {number} diff --git a/dist/mapper.js b/dist/mapper.js index 3d03078..68780de 100644 --- a/dist/mapper.js +++ b/dist/mapper.js @@ -1,6 +1,7 @@ "use strict"; exports.__esModule = true; exports.Mapper = exports.Region = void 0; +var goal_js_1 = require("./goal.js"); var physics = require("./pb/physics_pb.js"); var lugo = require("./pb/server_pb.js"); var Point = physics.Point; @@ -129,6 +130,18 @@ var Mapper = /** @class */ (function () { this.regionWidth = specs_js_1.SPECS.MAX_X_COORDINATE / cols; this.regionHeight = specs_js_1.SPECS.MAX_Y_COORDINATE / rows; } + Mapper.prototype.getDefenseGoal = function () { + if (this.side === lugo.Team.Side.HOME) { + return goal_js_1.HOME_GOAL; + } + return goal_js_1.AWAY_GOAL; + }; + Mapper.prototype.getAttackGoal = function () { + if (this.side === lugo.Team.Side.AWAY) { + return goal_js_1.AWAY_GOAL; + } + return goal_js_1.HOME_GOAL; + }; /** * @param col {number} * @param row {number} diff --git a/dist/rl/gym.d.ts b/dist/rl/gym.d.ts index 73a2d63..87c4e0a 100644 --- a/dist/rl/gym.d.ts +++ b/dist/rl/gym.d.ts @@ -1,6 +1,6 @@ -import { RemoteControl } from "./remoteControl"; -import { BotTrainer, TrainingFunction } from "./interfaces"; import { Client } from '../client'; +import { BotTrainer, TrainingFunction } from "./interfaces"; +import { RemoteControl } from "./remoteControl"; export declare class Gym { private trainingCrl; private gameServerAddress; diff --git a/dist/rl/gym.js b/dist/rl/gym.js index df635dd..7401e3a 100644 --- a/dist/rl/gym.js +++ b/dist/rl/gym.js @@ -37,9 +37,9 @@ var __generator = (this && this.__generator) || function (thisArg, body) { }; exports.__esModule = true; exports.Gym = void 0; -var trainingCrl_1 = require("./trainingCrl"); -var helper_bots_1 = require("./helper_bots"); var server_pb_js_1 = require("../pb/server_pb.js"); +var helper_bots_1 = require("./helper_bots"); +var trainingCrl_1 = require("./trainingCrl"); var Gym = /** @class */ (function () { function Gym(remoteControl, trainer, trainingFunction, options) { if (options === void 0) { options = { debugging_log: false }; } @@ -55,9 +55,9 @@ var Gym = /** @class */ (function () { switch (_a.label) { case 0: hasStarted = false; - return [4 /*yield*/, lugoClient.play(function (orderSet, snapshot) { + return [4 /*yield*/, lugoClient.play(function (snapshot) { hasStarted = true; - return _this.trainingCrl.gameTurnHandler(orderSet, snapshot); + return _this.trainingCrl.gameTurnHandler(snapshot); }, function () { return __awaiter(_this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { diff --git a/dist/rl/helper_bots.js b/dist/rl/helper_bots.js index ac88b68..0a18412 100644 --- a/dist/rl/helper_bots.js +++ b/dist/rl/helper_bots.js @@ -37,9 +37,9 @@ var __generator = (this && this.__generator) || function (thisArg, body) { }; exports.__esModule = true; exports.newCustomHelperPlayer = exports.newZombieHelperPlayer = exports.newChaserHelperPlayer = exports.newRandomMotionHelperPlayer = void 0; -var mapper_1 = require("../mapper"); var client_1 = require("../client"); var index_1 = require("../index"); +var mapper_1 = require("../mapper"); var PLAYER_POSITIONS = { 1: { Col: 0, Row: 1 }, 2: { Col: 1, Row: 1 }, @@ -59,8 +59,8 @@ function newRandomMotionHelperPlayer(teamSide, playerNumber, gameServerAddress, if (turnsToChangeDirection === void 0) { turnsToChangeDirection = 60; } var changeCounter = 0; var currentDir; - return newCustomHelperPlayer(teamSide, playerNumber, gameServerAddress, function (orderSet, snapshot) { return __awaiter(_this, void 0, void 0, function () { - var reader; + return newCustomHelperPlayer(teamSide, playerNumber, gameServerAddress, function (snapshot) { return __awaiter(_this, void 0, void 0, function () { + var orders, debug_message; return __generator(this, function (_a) { changeCounter--; if (changeCounter <= 0) { @@ -77,38 +77,39 @@ function newRandomMotionHelperPlayer(teamSide, playerNumber, gameServerAddress, index_1.DIRECTION.FORWARD_LEFT, ][Math.floor(Math.random() * 8)]; } - reader = new index_1.GameSnapshotReader(snapshot, teamSide); - orderSet.addOrders(reader.makeOrderMoveByDirection(currentDir)); - orderSet.setDebugMessage("".concat(teamSide === 0 ? 'HOME' : 'AWAY', "-").concat(playerNumber, " #").concat(snapshot.getTurn(), " - chasing ball")); - return [2 /*return*/, orderSet]; + orders = [snapshot.makeOrderMoveByDirection(currentDir)]; + debug_message = "".concat(teamSide === 0 ? 'HOME' : 'AWAY', "-").concat(playerNumber, " #").concat(snapshot.getTurn(), " - chasing ball"); + return [2 /*return*/, { orders: orders, debug_message: debug_message }]; }); }); }); } exports.newRandomMotionHelperPlayer = newRandomMotionHelperPlayer; function newChaserHelperPlayer(teamSide, playerNumber, gameServerAddress) { var _this = this; - return newCustomHelperPlayer(teamSide, playerNumber, gameServerAddress, function (orderSet, snapshot) { return __awaiter(_this, void 0, void 0, function () { - var reader, me; + return newCustomHelperPlayer(teamSide, playerNumber, gameServerAddress, function (snapshot) { return __awaiter(_this, void 0, void 0, function () { + var orders, me, debug_message; return __generator(this, function (_a) { - reader = new index_1.GameSnapshotReader(snapshot, teamSide); - orderSet.addOrders(reader.makeOrderCatch()); - me = reader.getPlayer(teamSide, playerNumber); + orders = []; + orders.push(snapshot.makeOrderCatch()); + me = snapshot.getPlayer(teamSide, playerNumber); if (!me) { throw new Error("did not find myself in the game"); } - orderSet.addOrders(reader.makeOrderMoveMaxSpeed(me.getPosition(), snapshot.getBall().getPosition())); - orderSet.setDebugMessage("".concat(teamSide === 0 ? 'HOME' : 'AWAY', "-").concat(playerNumber, " #").concat(snapshot.getTurn(), " - chasing ball")); - return [2 /*return*/, orderSet]; + orders.push(snapshot.makeOrderMoveMaxSpeed(snapshot.getBall().getPosition())); + debug_message = "".concat(teamSide === 0 ? 'HOME' : 'AWAY', "-").concat(playerNumber, " #").concat(snapshot.getTurn(), " - chasing ball"); + return [2 /*return*/, { orders: orders, debug_message: debug_message }]; }); }); }); } exports.newChaserHelperPlayer = newChaserHelperPlayer; function newZombieHelperPlayer(teamSide, playerNumber, gameServerAddress) { var _this = this; - return newCustomHelperPlayer(teamSide, playerNumber, gameServerAddress, function (orderSet, snapshot) { return __awaiter(_this, void 0, void 0, function () { + return newCustomHelperPlayer(teamSide, playerNumber, gameServerAddress, function (snapshot) { return __awaiter(_this, void 0, void 0, function () { + var orders, debug_message; return __generator(this, function (_a) { - orderSet.setDebugMessage("".concat(teamSide === 0 ? 'HOME' : 'AWAY', "-").concat(playerNumber, " #").concat(snapshot.getTurn())); - return [2 /*return*/, orderSet]; + orders = []; + debug_message = "".concat(teamSide === 0 ? 'HOME' : 'AWAY', "-").concat(playerNumber, " #").concat(snapshot.getTurn()); + return [2 /*return*/, { orders: orders, debug_message: debug_message }]; }); }); }); } diff --git a/dist/rl/trainingCrl.d.ts b/dist/rl/trainingCrl.d.ts index 9fb9145..df4a0ac 100644 --- a/dist/rl/trainingCrl.d.ts +++ b/dist/rl/trainingCrl.d.ts @@ -1,6 +1,7 @@ -import { RemoteControl } from "./remoteControl"; +import GameSnapshotInspector from "../game-snapshot-inspector"; +import { GameSnapshot, Order } from "../pb/server_pb"; import { BotTrainer, TrainingController, TrainingFunction } from './interfaces'; -import { GameSnapshot, OrderSet } from "../pb/server_pb"; +import { RemoteControl } from "./remoteControl"; export declare const delay: (ms: any) => Promise; export declare class TrainingCrl implements TrainingController { /** @@ -36,7 +37,10 @@ export declare class TrainingCrl implements TrainingController { done: boolean; }>; _gotNextState: (newGameSnapshot: GameSnapshot) => void; - gameTurnHandler(orderSet: any, snapshot: any): Promise; + gameTurnHandler(snapshot: GameSnapshotInspector): Promise; waitUntilNextListeningState(): Promise; stop(): Promise; _debug(msg: any): void; diff --git a/dist/rl/trainingCrl.js b/dist/rl/trainingCrl.js index ad439ea..6adfd6c 100644 --- a/dist/rl/trainingCrl.js +++ b/dist/rl/trainingCrl.js @@ -37,6 +37,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) { }; exports.__esModule = true; exports.TrainingCrl = exports.delay = void 0; +var server_pb_1 = require("../pb/server_pb"); var delay = function (ms) { return new Promise(function (resolve) { return setTimeout(resolve, ms); }); }; exports.delay = delay; var TrainingCrl = /** @class */ (function () { @@ -146,7 +147,7 @@ var TrainingCrl = /** @class */ (function () { }); }); }; - TrainingCrl.prototype.gameTurnHandler = function (orderSet, snapshot) { + TrainingCrl.prototype.gameTurnHandler = function (snapshot) { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { @@ -166,14 +167,14 @@ var TrainingCrl = /** @class */ (function () { if (this.onListeningMode) { throw new Error("faulty synchrony - got new turn while the trainer already was in listening mode (check the lugo 'timer-mode')"); } - this._gotNextState(snapshot); + this._gotNextState(snapshot.getSnapshot()); return [4 /*yield*/, new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () { var maxWait; var _this = this; return __generator(this, function (_a) { maxWait = setTimeout(function () { if (_this.stopRequested) { - return resolve(orderSet); + return resolve([]); } console.error("max wait for a new action"); reject(); @@ -181,11 +182,11 @@ var TrainingCrl = /** @class */ (function () { // [explaining flow] here we check if the bot asked to stop if (this.stopRequested) { this._debug("stop requested - will not defined call back for new actions"); - resolve(orderSet); + resolve([]); clearTimeout(maxWait); return [2 /*return*/, null]; } - this.OrderSet = orderSet; + this.OrderSet = new server_pb_1.OrderSet(); // [explaining flow] here we will define the callback called when the bot returns the orders. this.resumeListeningPhase = function (updatedOrderSet) { _this._debug("sending new action"); diff --git a/dist/starter.d.ts b/dist/starter.d.ts new file mode 100644 index 0000000..1896c29 --- /dev/null +++ b/dist/starter.d.ts @@ -0,0 +1,21 @@ +import { Bot } from "."; +import { RawTurnProcessor } from "./client"; +import { EnvVarLoader } from "./configurator"; +import { Mapper } from "./mapper"; +import { Point } from "./proto_exported"; +export declare function NewDefaultStarter(): Starter; +declare class Starter { + private initial_position; + private config; + private mapper; + constructor(initial_position: Point, config: EnvVarLoader, mapper: Mapper); + getMapper(): Mapper; + setMapper(mapper: Mapper): void; + getInitialPosition(): Point; + setInitialPosition(initial_position: Point): void; + getConfig(): EnvVarLoader; + setConfig(config: EnvVarLoader): void; + run(bot: Bot, onJoin?: () => void): void; + runJustTurnHandler(raw_processor: RawTurnProcessor, onJoin?: () => void): void; +} +export {}; diff --git a/dist/starter.js b/dist/starter.js new file mode 100644 index 0000000..b5e199b --- /dev/null +++ b/dist/starter.js @@ -0,0 +1,52 @@ +"use strict"; +exports.__esModule = true; +exports.NewDefaultStarter = void 0; +var _1 = require("."); +var defaults_1 = require("./util/defaults"); +function NewDefaultStarter() { + var _a = (0, defaults_1.DefaultInitBundle)(), defaultConfig = _a.defaultConfig, defaultMapper = _a.defaultMapper, defaultInitialPosition = _a.defaultInitialPosition; + return new Starter(defaultInitialPosition, defaultConfig, defaultMapper); +} +exports.NewDefaultStarter = NewDefaultStarter; +var Starter = /** @class */ (function () { + function Starter(initial_position, config, mapper) { + this.initial_position = initial_position; + this.config = config; + this.mapper = mapper; + } + Starter.prototype.getMapper = function () { + return this.mapper; + }; + Starter.prototype.setMapper = function (mapper) { + this.mapper = mapper; + }; + Starter.prototype.getInitialPosition = function () { + return this.initial_position; + }; + Starter.prototype.setInitialPosition = function (initial_position) { + this.initial_position = initial_position; + }; + Starter.prototype.getConfig = function () { + return this.config; + }; + Starter.prototype.setConfig = function (config) { + this.config = config; + }; + Starter.prototype.run = function (bot, onJoin) { + var lugoClient = (0, _1.NewClientFromConfig)(this.config, this.initial_position); + lugoClient.playAsBot(bot, onJoin).then(function () { + console.log("all done"); + })["catch"](function (e) { + console.error(e); + }); + }; + Starter.prototype.runJustTurnHandler = function (raw_processor, onJoin) { + var lugoClient = (0, _1.NewClientFromConfig)(this.config, this.initial_position); + lugoClient.play(raw_processor, onJoin).then(function () { + console.log("all done"); + })["catch"](function (e) { + console.error(e); + }); + }; + return Starter; +}()); diff --git a/dist/stub.d.ts b/dist/stub.d.ts index 44f8e13..6a6cadb 100644 --- a/dist/stub.d.ts +++ b/dist/stub.d.ts @@ -1,4 +1,5 @@ -import { GameSnapshot, OrderSet } from './pb/server_pb.js'; +import GameSnapshotInspector from './game-snapshot-inspector.js'; +import { Order } from './pb/server_pb.js'; export declare enum PLAYER_STATE { SUPPORTING = "supporting", HOLDING_THE_BALL = "holding", @@ -9,48 +10,58 @@ export interface Bot { /** * OnDisputing is called when no one has the ball possession * - * @param {OrderSet} orderSet - * @param {GameSnapshot} snapshot - * @returns {OrderSet | null} + * @param {GameSnapshotInspector} inspector + * @returns {Order[] | { orders: Order[], debug_message: string } | null} */ - onDisputing: (orderSet: OrderSet, snapshot: GameSnapshot) => OrderSet | null; + onDisputing: (inspector: GameSnapshotInspector) => Order[] | { + orders: Order[]; + debug_message: string; + } | null; /** * OnDefending is called when an opponent player has the ball possession * - * @param {OrderSet} orderSet - * @param {GameSnapshot} snapshot - * @returns {OrderSet | null} + * @param {GameSnapshotInspector} inspector + * @returns {Order[] | { orders: Order[], debug_message: string } | null} */ - onDefending: (orderSet: OrderSet, snapshot: GameSnapshot) => OrderSet | null; + onDefending: (inspector: GameSnapshotInspector) => Order[] | { + orders: Order[]; + debug_message: string; + } | null; /** * OnHolding is called when this bot has the ball possession * - * @param {OrderSet} orderSet - * @param {GameSnapshot} snapshot - * @returns {OrderSet | null} + * @param {GameSnapshotInspector} inspector + * @returns {Order[] | { orders: Order[], debug_message: string } | null} */ - onHolding: (orderSet: OrderSet, snapshot: GameSnapshot) => OrderSet | null; + onHolding: (inspector: GameSnapshotInspector) => Order[] | { + orders: Order[]; + debug_message: string; + } | null; /** * OnSupporting is called when a teammate player has the ball possession * - * @param {OrderSet} orderSet - * @param {GameSnapshot} snapshot - * @returns {OrderSet | null} + * @param {GameSnapshotInspector} inspector + * @returns {Order[] | { orders: Order[], debug_message: string } | null} */ - onSupporting: (orderSet: OrderSet, snapshot: GameSnapshot) => OrderSet | null; + onSupporting: (inspector: GameSnapshotInspector) => Order[] | { + orders: Order[]; + debug_message: string; + } | null; /** * AsGoalkeeper is only called when this bot is the goalkeeper (number 1). This method is called on every turn, * and the player state is passed at the last parameter. - * @param {OrderSet} orderSet - * @param {GameSnapshot} snapshot + * @param {GameSnapshotInspector} inspector * @param {PLAYER_STATE} state - * @returns {OrderSet | null} + * @returns {Order[] | { orders: Order[], debug_message: string } | null} */ - asGoalkeeper: (orderSet: OrderSet, snapshot: GameSnapshot, state: PLAYER_STATE) => OrderSet | null; + asGoalkeeper: (inspector: GameSnapshotInspector, state: PLAYER_STATE) => Order[] | { + orders: Order[]; + debug_message: string; + } | null; /** * gettingReady is called when the game is on Getting Ready state. * - * @param snapshot + * @param {GameSnapshotInspector} inspector */ - gettingReady: (snapshot: GameSnapshot) => void; + gettingReady: (inspector: GameSnapshotInspector) => void; } diff --git a/dist/util/defaults.d.ts b/dist/util/defaults.d.ts new file mode 100644 index 0000000..9977722 --- /dev/null +++ b/dist/util/defaults.d.ts @@ -0,0 +1,54 @@ +import { EnvVarLoader } from "../configurator"; +import { Mapper } from "../mapper"; +import { Point } from "../proto_exported"; +export declare function DefaultInitBundle(): { + defaultConfig: EnvVarLoader; + defaultMapper: Mapper; + defaultInitialPosition: Point; +}; +export declare const DEFAULT_PLAYER_POSITIONS: { + 1: { + Col: number; + Row: number; + }; + 2: { + Col: number; + Row: number; + }; + 3: { + Col: number; + Row: number; + }; + 4: { + Col: number; + Row: number; + }; + 5: { + Col: number; + Row: number; + }; + 6: { + Col: number; + Row: number; + }; + 7: { + Col: number; + Row: number; + }; + 8: { + Col: number; + Row: number; + }; + 9: { + Col: number; + Row: number; + }; + 10: { + Col: number; + Row: number; + }; + 11: { + Col: number; + Row: number; + }; +}; diff --git a/dist/util/defaults.js b/dist/util/defaults.js new file mode 100644 index 0000000..491b961 --- /dev/null +++ b/dist/util/defaults.js @@ -0,0 +1,29 @@ +"use strict"; +exports.__esModule = true; +exports.DEFAULT_PLAYER_POSITIONS = exports.DefaultInitBundle = void 0; +var configurator_1 = require("../configurator"); +var mapper_1 = require("../mapper"); +function DefaultInitBundle() { + // the map will help us to see the field in quadrants (called regions) instead of working with coordinates + var defaultConfig = new configurator_1.EnvVarLoader(); + // the map will help us to see the field in quadrants (called regions) instead of working with coordinates + var defaultMapper = new mapper_1.Mapper(10, 6, defaultConfig.getBotTeamSide()); + // our bot strategy defines our bot initial position based on its number + var initialRegion = defaultMapper.getRegion(exports.DEFAULT_PLAYER_POSITIONS[defaultConfig.getBotNumber()].Col, exports.DEFAULT_PLAYER_POSITIONS[defaultConfig.getBotNumber()].Row); + var defaultInitialPosition = initialRegion.getCenter(); + return { defaultConfig: defaultConfig, defaultMapper: defaultMapper, defaultInitialPosition: defaultInitialPosition }; +} +exports.DefaultInitBundle = DefaultInitBundle; +exports.DEFAULT_PLAYER_POSITIONS = { + 1: { Col: 0, Row: 0 }, + 2: { Col: 1, Row: 1 }, + 3: { Col: 2, Row: 2 }, + 4: { Col: 2, Row: 3 }, + 5: { Col: 1, Row: 4 }, + 6: { Col: 3, Row: 1 }, + 7: { Col: 3, Row: 2 }, + 8: { Col: 3, Row: 3 }, + 9: { Col: 3, Row: 4 }, + 10: { Col: 4, Row: 3 }, + 11: { Col: 4, Row: 2 } +}; diff --git a/example/typescript/dist/main.js b/example/typescript/dist/main.js index 4a3cdff..e11d02c 100644 --- a/example/typescript/dist/main.js +++ b/example/typescript/dist/main.js @@ -2,19 +2,21 @@ exports.__esModule = true; var lugo4node_1 = require("@lugobots/lugo4node"); var myBot_js_1 = require("./myBot.js"); -// we must load the env vars following the standard defined by the game specs because all bots will receive the -// arguments in the same format (env vars) -var config = new lugo4node_1.EnvVarLoader(); +var starter = (0, lugo4node_1.NewDefaultStarter)(); +// starter.setConfig(config) +// starter.setInitialPosition(position) +// starter.setMapper(mapper) +starter.run(new myBot_js_1.MyBot(starter.getConfig().getBotTeamSide(), starter.getConfig().getBotNumber(), starter.getInitialPosition(), starter.getMapper())); +// ADVANCED // the map will help us to see the field in quadrants (called regions) instead of working with coordinates -var map = new lugo4node_1.Mapper(10, 6, config.getBotTeamSide()); +// const config = new EnvVarLoader() +// the map will help us to see the field in quadrants (called regions) instead of working with coordinates +// const map = new Mapper(10, 6, config.getBotTeamSide()) // our bot strategy defines our bot initial position based on its number -var initialRegion = map.getRegion(myBot_js_1.PLAYER_POSITIONS[config.getBotNumber()].Col, myBot_js_1.PLAYER_POSITIONS[config.getBotNumber()].Row); -// now we can create the bot. We will use a shortcut to create the client from the config, but we could use the -// client constructor as well -var lugoClient = (0, lugo4node_1.NewClientFromConfig)(config, initialRegion.getCenter()); -var myBot = new myBot_js_1.MyBot(config.getBotTeamSide(), config.getBotNumber(), initialRegion.getCenter(), map); -lugoClient.playAsBot(myBot).then(function () { - console.log("all done"); -})["catch"](function (e) { - console.error(e); -}); +// const initialRegion = map.getRegion(DEFAULT_PLAYER_POSITIONS[config.getBotNumber()].Col, DEFAULT_PLAYER_POSITIONS[config.getBotNumber()].Row) +// const lugoClient = NewClientFromConfig(config, initialRegion.getCenter()) +// lugoClient.playAsBot(bot).then(() => { +// console.log(`all done`) +// }).catch(e => { +// console.error(e) +// }) diff --git a/example/typescript/dist/myBot.js b/example/typescript/dist/myBot.js index 2d89a42..9b39e8d 100644 --- a/example/typescript/dist/myBot.js +++ b/example/typescript/dist/myBot.js @@ -16,46 +16,34 @@ var MyBot = /** @class */ (function () { this.initPosition = initPosition; mapper.getRegionFromPoint(initPosition); } - MyBot.prototype.makeReader = function (snapshot) { - var reader = new lugo4node_1.GameSnapshotReader(snapshot, this.side); - var me = reader.getPlayer(this.side, this.number); - if (!me) { - throw new Error("did not find myself in the game"); - } - return { reader: reader, me: me }; - }; MyBot.prototype.isNear = function (regionOrigin, destOrigin) { var maxDistance = 2; return Math.abs(regionOrigin.getRow() - destOrigin.getRow()) <= maxDistance && Math.abs(regionOrigin.getCol() - destOrigin.getCol()) <= maxDistance; }; - MyBot.prototype.onDisputing = function (orderSet, snapshot) { + MyBot.prototype.onDisputing = function (inspector) { try { // the Lugo.GameSnapshot helps us to read the game state - var _a = this.makeReader(snapshot), reader = _a.reader, me = _a.me; - var ballPosition = reader.getBall().getPosition(); + var orders = []; + var me = inspector.getMe(); + var ballPosition = inspector.getBall().getPosition(); var ballRegion = this.mapper.getRegionFromPoint(ballPosition); var myRegion = this.mapper.getRegionFromPoint(me.getPosition()); - //by default, let's stay on our region - var moveDestination = this.initPosition; // but if the ball is near to me, I will try to catch it if (this.isNear(ballRegion, myRegion)) { - moveDestination = ballPosition; + orders.push(inspector.makeOrderMoveMaxSpeed(ballPosition)); } - var moveOrder = reader.makeOrderMoveMaxSpeed(me.getPosition(), moveDestination); - // we can ALWAYS try to catch the ball - var catchOrder = reader.makeOrderCatch(); - orderSet.setOrdersList([moveOrder, catchOrder]); - return orderSet; + orders.push(inspector.makeOrderCatch()); + return orders; } catch (e) { console.log("did not play this turn", e); } }; - MyBot.prototype.onDefending = function (orderSet, snapshot) { + MyBot.prototype.onDefending = function (inspector) { try { - var _a = this.makeReader(snapshot), reader = _a.reader, me = _a.me; - var ballPosition = snapshot.getBall().getPosition(); + var me = inspector.getMe(); + var ballPosition = inspector.getBall().getPosition(); var ballRegion = this.mapper.getRegionFromPoint(ballPosition); var myRegion = this.mapper.getRegionFromPoint(this.initPosition); var moveDest = this.initPosition; @@ -63,75 +51,59 @@ var MyBot = /** @class */ (function () { Math.abs(myRegion.getCol() - ballRegion.getCol()) <= 2) { moveDest = ballPosition; } - var moveOrder = reader.makeOrderMoveMaxSpeed(me.getPosition(), moveDest); - var catchOrder = reader.makeOrderCatch(); - var orderSet_1 = new lugo4node_1.Lugo.OrderSet(); - orderSet_1.setTurn(snapshot.getTurn()); - orderSet_1.setDebugMessage("trying to catch the ball"); - orderSet_1.setOrdersList([moveOrder, catchOrder]); - return orderSet_1; + var moveOrder = inspector.makeOrderMoveMaxSpeed(moveDest); + var catchOrder = inspector.makeOrderCatch(); + return { orders: [moveOrder, catchOrder], debug_message: "trying to catch the ball" }; } catch (e) { console.log("did not play this turn", e); } }; - MyBot.prototype.onHolding = function (orderSet, snapshot) { + MyBot.prototype.onHolding = function (inspector) { try { - var _a = this.makeReader(snapshot), reader = _a.reader, me = _a.me; - var myGoalCenter = this.mapper.getRegionFromPoint(reader.getOpponentGoal().getCenter()); + var me = inspector.getMe(); + var myGoalCenter = this.mapper.getRegionFromPoint(this.mapper.getAttackGoal().getCenter()); var currentRegion = this.mapper.getRegionFromPoint(me.getPosition()); var myOrder = void 0; if (Math.abs(currentRegion.getRow() - myGoalCenter.getRow()) <= 1 && Math.abs(currentRegion.getCol() - myGoalCenter.getCol()) <= 1) { - myOrder = reader.makeOrderKickMaxSpeed(snapshot.getBall(), reader.getOpponentGoal().getCenter()); + myOrder = inspector.makeOrderKickMaxSpeed(this.mapper.getAttackGoal().getCenter()); } else { - myOrder = reader.makeOrderMoveMaxSpeed(me.getPosition(), reader.getOpponentGoal().getCenter()); + myOrder = inspector.makeOrderMoveMaxSpeed(this.mapper.getAttackGoal().getCenter()); } - var orderSet_2 = new lugo4node_1.Lugo.OrderSet(); - orderSet_2.setTurn(snapshot.getTurn()); - orderSet_2.setDebugMessage("attack!"); - orderSet_2.setOrdersList([myOrder]); - return orderSet_2; + return { orders: [myOrder], debug_message: "attack!" }; } catch (e) { console.log("did not play this turn", e); } }; - MyBot.prototype.onSupporting = function (orderSet, snapshot) { + MyBot.prototype.onSupporting = function (inspector) { try { - var _a = this.makeReader(snapshot), reader = _a.reader, me = _a.me; - var ballHolderPosition = snapshot.getBall().getPosition(); - var myOrder = reader.makeOrderMoveMaxSpeed(me.getPosition(), ballHolderPosition); - var orderSet_3 = new lugo4node_1.Lugo.OrderSet(); - orderSet_3.setTurn(snapshot.getTurn()); - orderSet_3.setDebugMessage("supporting"); - orderSet_3.setOrdersList([myOrder]); - return orderSet_3; + var me = inspector.getMe(); + var ballHolderPosition = inspector.getBall().getPosition(); + var myOrder = inspector.makeOrderMoveMaxSpeed(ballHolderPosition); + return { orders: [myOrder], debug_message: "supporting" }; } catch (e) { console.log("did not play this turn", e); } }; - MyBot.prototype.asGoalkeeper = function (orderSet, snapshot, state) { + MyBot.prototype.asGoalkeeper = function (inspector, state) { try { - var _a = this.makeReader(snapshot), reader = _a.reader, me = _a.me; - var position = snapshot.getBall().getPosition(); + var me = inspector.getMe(); + var position = inspector.getBall().getPosition(); if (state !== lugo4node_1.PLAYER_STATE.DISPUTING_THE_BALL) { - position = reader.getMyGoal().getCenter(); + position = this.mapper.getDefenseGoal().getCenter(); } - var myOrder = reader.makeOrderMoveMaxSpeed(me.getPosition(), position); - var orderSet_4 = new lugo4node_1.Lugo.OrderSet(); - orderSet_4.setTurn(snapshot.getTurn()); - orderSet_4.setDebugMessage("supporting"); - orderSet_4.setOrdersList([myOrder, reader.makeOrderCatch()]); - return orderSet_4; + var myOrder = inspector.makeOrderMoveMaxSpeed(position); + return { orders: [myOrder, inspector.makeOrderCatch()], debug_message: "supporting" }; } catch (e) { console.log("did not play this turn", e); } }; - MyBot.prototype.gettingReady = function (snapshot) { + MyBot.prototype.gettingReady = function (inspector) { }; ; return MyBot; diff --git a/example/typescript/dist/package.json b/example/typescript/dist/package.json deleted file mode 100644 index 5bbefff..0000000 --- a/example/typescript/dist/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "commonjs" -} diff --git a/example/typescript/docker-compose.yml b/example/typescript/docker-compose.yml index abd380d..ee26219 100644 --- a/example/typescript/docker-compose.yml +++ b/example/typescript/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.4' services: game_server: image: lugobots/server:${SERVER_VERSION:-latest} - command: play --dev-mode --timer-mode=wait + command: play --dev-mode --timer-mode=wait --quiet ports: - "5000:5000" - "8080:8080" diff --git a/example/typescript/package-lock.json b/example/typescript/package-lock.json index 18f8221..d46c94e 100644 --- a/example/typescript/package-lock.json +++ b/example/typescript/package-lock.json @@ -15,17 +15,18 @@ }, "../..": { "name": "@lugobots/lugo4node", - "version": "0.0.1-beta.7", + "version": "1.0.1-beta.2", "license": "ISC", "dependencies": { "@grpc/grpc-js": "^1.6.7", "google-protobuf": "^3.20.1", "process": "^0.11.10", - "typescript": "^4.7.4" + "typescript": "^4.9.4" }, "devDependencies": { "@grpc/proto-loader": "^0.6.12", - "mocha": "^10.0.0" + "mocha": "^10.0.0", + "tsc-watch": "^6.0.0" } }, "node_modules/@lugobots/lugo4node": { @@ -54,7 +55,8 @@ "google-protobuf": "^3.20.1", "mocha": "^10.0.0", "process": "^0.11.10", - "typescript": "^4.7.4" + "tsc-watch": "^6.0.0", + "typescript": "^4.9.4" } }, "typescript": { diff --git a/example/typescript/package.json b/example/typescript/package.json index 4098017..7d91c07 100644 --- a/example/typescript/package.json +++ b/example/typescript/package.json @@ -8,7 +8,6 @@ "play": "cd ../../ && docker build -t example-bot-typescript -f example/typescript/Dockerfile . && cd - && docker-compose up", "tsc": "tsc" }, - "type": "module", "author": "Rubens Silva", "license": "ISC", "dependencies": { diff --git a/example/typescript/src/main.ts b/example/typescript/src/main.ts index b34c7a3..8d38ea5 100644 --- a/example/typescript/src/main.ts +++ b/example/typescript/src/main.ts @@ -1,30 +1,34 @@ -import {EnvVarLoader, NewClientFromConfig, Mapper} from '@lugobots/lugo4node' -import {MyBot, PLAYER_POSITIONS} from './myBot.js' +import { NewDefaultStarter } from '@lugobots/lugo4node' +import { MyBot } from './myBot.js' -// we must load the env vars following the standard defined by the game specs because all bots will receive the -// arguments in the same format (env vars) -const config = new EnvVarLoader() +const starter = NewDefaultStarter() -// the map will help us to see the field in quadrants (called regions) instead of working with coordinates -const map = new Mapper(10, 6, config.getBotTeamSide()) +// starter.setConfig(config) +// starter.setInitialPosition(position) +// starter.setMapper(mapper) -// our bot strategy defines our bot initial position based on its number -const initialRegion = map.getRegion(PLAYER_POSITIONS[config.getBotNumber()].Col, PLAYER_POSITIONS[config.getBotNumber()].Row) +starter.run(new MyBot( + starter.getConfig().getBotTeamSide(), + starter.getConfig().getBotNumber(), + starter.getInitialPosition(), + starter.getMapper() +)); -// now we can create the bot. We will use a shortcut to create the client from the config, but we could use the -// client constructor as well -const lugoClient = NewClientFromConfig(config, initialRegion.getCenter()) -const myBot = new MyBot( - config.getBotTeamSide(), - config.getBotNumber(), - initialRegion.getCenter(), - map, -) +// ADVANCED +// the map will help us to see the field in quadrants (called regions) instead of working with coordinates +// const config = new EnvVarLoader() + +// the map will help us to see the field in quadrants (called regions) instead of working with coordinates +// const map = new Mapper(10, 6, config.getBotTeamSide()) + +// our bot strategy defines our bot initial position based on its number +// const initialRegion = map.getRegion(DEFAULT_PLAYER_POSITIONS[config.getBotNumber()].Col, DEFAULT_PLAYER_POSITIONS[config.getBotNumber()].Row) -lugoClient.playAsBot(myBot).then(() => { - console.log(`all done`) -}).catch(e => { - console.error(e) -}) +// const lugoClient = NewClientFromConfig(config, initialRegion.getCenter()) +// lugoClient.playAsBot(bot).then(() => { +// console.log(`all done`) +// }).catch(e => { +// console.error(e) +// }) diff --git a/example/typescript/src/myBot.ts b/example/typescript/src/myBot.ts index 3f67673..8e3df44 100644 --- a/example/typescript/src/myBot.ts +++ b/example/typescript/src/myBot.ts @@ -1,4 +1,4 @@ -import {Bot, GameSnapshotReader, Lugo, Mapper, PLAYER_STATE, Region} from '@lugobots/lugo4node' +import { Bot, GameSnapshotInspector, Lugo, Mapper, PLAYER_STATE, Region } from '@lugobots/lugo4node'; export class MyBot implements Bot { @@ -23,54 +23,40 @@ export class MyBot implements Bot { mapper.getRegionFromPoint(initPosition) } - private makeReader(snapshot: Lugo.GameSnapshot): { reader: GameSnapshotReader, me: Lugo.Player } { - const reader = new GameSnapshotReader(snapshot, this.side) - const me = reader.getPlayer(this.side, this.number) - if (!me) { - throw new Error("did not find myself in the game") - } - return {reader, me} - } - private isNear(regionOrigin : Region, destOrigin: Region) : boolean { const maxDistance = 2 return Math.abs(regionOrigin.getRow() - destOrigin.getRow()) <= maxDistance && Math.abs(regionOrigin.getCol() - destOrigin.getCol()) <= maxDistance } - onDisputing(orderSet: Lugo.OrderSet, snapshot: Lugo.GameSnapshot): Lugo.OrderSet | null { + onDisputing(inspector: GameSnapshotInspector): Lugo.Order[] | { orders: Lugo.Order[], debug_message: string } | null { try { // the Lugo.GameSnapshot helps us to read the game state - const {reader, me} = this.makeReader(snapshot) - const ballPosition = reader.getBall().getPosition() + const orders = []; + const me = inspector.getMe(); + const ballPosition = inspector.getBall().getPosition() const ballRegion = this.mapper.getRegionFromPoint(ballPosition) const myRegion = this.mapper.getRegionFromPoint(me.getPosition()) - //by default, let's stay on our region - let moveDestination = this.initPosition // but if the ball is near to me, I will try to catch it if (this.isNear(ballRegion, myRegion)) { - moveDestination = ballPosition + orders.push(inspector.makeOrderMoveMaxSpeed( ballPosition)); } - const moveOrder = reader.makeOrderMoveMaxSpeed(me.getPosition(), moveDestination) - - // we can ALWAYS try to catch the ball - const catchOrder = reader.makeOrderCatch() + orders.push(inspector.makeOrderCatch()); - orderSet.setOrdersList([moveOrder, catchOrder]) - return orderSet + return orders; } catch (e) { console.log(`did not play this turn`, e) } } - onDefending(orderSet: Lugo.OrderSet, snapshot: Lugo.GameSnapshot): Lugo.OrderSet | null { + onDefending(inspector: GameSnapshotInspector): Lugo.Order[] | { orders: Lugo.Order[], debug_message: string } | null { try { - const {reader, me} = this.makeReader(snapshot) - const ballPosition = snapshot.getBall().getPosition() + const me = inspector.getMe(); + const ballPosition = inspector.getBall().getPosition() const ballRegion = this.mapper.getRegionFromPoint(ballPosition) const myRegion = this.mapper.getRegionFromPoint(this.initPosition) @@ -79,81 +65,67 @@ export class MyBot implements Bot { Math.abs(myRegion.getCol() - ballRegion.getCol()) <= 2) { moveDest = ballPosition } - const moveOrder = reader.makeOrderMoveMaxSpeed(me.getPosition(), moveDest) - const catchOrder = reader.makeOrderCatch() - - const orderSet = new Lugo.OrderSet() - orderSet.setTurn(snapshot.getTurn()) - orderSet.setDebugMessage("trying to catch the ball") - orderSet.setOrdersList([moveOrder, catchOrder]) - return orderSet + const moveOrder = inspector.makeOrderMoveMaxSpeed( moveDest) + const catchOrder = inspector.makeOrderCatch() + + return { orders: [moveOrder, catchOrder], debug_message: "trying to catch the ball" } } catch (e) { console.log(`did not play this turn`, e) } } - onHolding(orderSet: Lugo.OrderSet, snapshot: Lugo.GameSnapshot): Lugo.OrderSet | null { + onHolding(inspector: GameSnapshotInspector): Lugo.Order[] | { orders: Lugo.Order[], debug_message: string } | null { try { - const {reader, me} = this.makeReader(snapshot) + const me = inspector.getMe(); - const myGoalCenter = this.mapper.getRegionFromPoint(reader.getOpponentGoal().getCenter()) + const myGoalCenter = this.mapper.getRegionFromPoint(this.mapper.getAttackGoal().getCenter()) const currentRegion = this.mapper.getRegionFromPoint(me.getPosition()) let myOrder; if (Math.abs(currentRegion.getRow() - myGoalCenter.getRow()) <= 1 && Math.abs(currentRegion.getCol() - myGoalCenter.getCol()) <= 1) { - myOrder = reader.makeOrderKickMaxSpeed(snapshot.getBall(), reader.getOpponentGoal().getCenter()) + myOrder = inspector.makeOrderKickMaxSpeed(this.mapper.getAttackGoal().getCenter()) } else { - myOrder = reader.makeOrderMoveMaxSpeed(me.getPosition(), reader.getOpponentGoal().getCenter()) + myOrder = inspector.makeOrderMoveMaxSpeed(this.mapper.getAttackGoal().getCenter()) } - const orderSet = new Lugo.OrderSet() - orderSet.setTurn(snapshot.getTurn()) - orderSet.setDebugMessage("attack!") - orderSet.setOrdersList([myOrder]) - return orderSet + + return { orders: [myOrder], debug_message: "attack!" } } catch (e) { console.log(`did not play this turn`, e) } } - onSupporting(orderSet: Lugo.OrderSet, snapshot: Lugo.GameSnapshot): Lugo.OrderSet | null { + onSupporting(inspector: GameSnapshotInspector): Lugo.Order[] | { orders: Lugo.Order[], debug_message: string } | null { try { - const {reader, me} = this.makeReader(snapshot) - const ballHolderPosition = snapshot.getBall().getPosition() - const myOrder = reader.makeOrderMoveMaxSpeed(me.getPosition(), ballHolderPosition) - - const orderSet = new Lugo.OrderSet() - orderSet.setTurn(snapshot.getTurn()) - orderSet.setDebugMessage("supporting") - orderSet.setOrdersList([myOrder]) - return orderSet + const me = inspector.getMe(); + const ballHolderPosition = inspector.getBall().getPosition() + const myOrder = inspector.makeOrderMoveMaxSpeed( ballHolderPosition) + + return { orders: [myOrder], debug_message: "supporting" } } catch (e) { console.log(`did not play this turn`, e) } } - asGoalkeeper(orderSet: Lugo.OrderSet, snapshot: Lugo.GameSnapshot, state: PLAYER_STATE): Lugo.OrderSet | null { + asGoalkeeper(inspector: GameSnapshotInspector, state: PLAYER_STATE): Lugo.Order[] | { orders: Lugo.Order[], debug_message: string } | null { try { - const {reader, me} = this.makeReader(snapshot) - let position = snapshot.getBall().getPosition() + const me = inspector.getMe(); + let position = inspector.getBall().getPosition() if (state !== PLAYER_STATE.DISPUTING_THE_BALL) { - position = reader.getMyGoal().getCenter() + position = this.mapper.getDefenseGoal().getCenter() } - const myOrder = reader.makeOrderMoveMaxSpeed(me.getPosition(), position) + const myOrder = inspector.makeOrderMoveMaxSpeed( position) + - const orderSet = new Lugo.OrderSet() - orderSet.setTurn(snapshot.getTurn()) - orderSet.setDebugMessage("supporting") - orderSet.setOrdersList([myOrder, reader.makeOrderCatch()]) - return orderSet + return { orders: [myOrder, inspector.makeOrderCatch()], debug_message: "supporting"} } catch (e) { console.log(`did not play this turn`, e) } } - gettingReady(snapshot: Lugo.GameSnapshot): void { + gettingReady(inspector: GameSnapshotInspector): void { }; } diff --git a/package-lock.json b/package-lock.json index 5f16e71..4cbdbf6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@lugobots/lugo4node", - "version": "1.0.1-beta.1", + "version": "1.0.1-beta.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@lugobots/lugo4node", - "version": "1.0.1-beta.1", + "version": "1.0.1-beta.2", "license": "ISC", "dependencies": { "@grpc/grpc-js": "^1.6.7", diff --git a/src/client.ts b/src/client.ts index d769ec4..d56c4f1 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,16 +1,18 @@ -import {Point} from "./pb/physics_pb.js" -import {GameSnapshot, JoinRequest, OrderSet, Team} from "./pb/server_pb.js" -import {GameClient} from './pb/server_grpc_pb' -import {credentials} from '@grpc/grpc-js' +import { credentials } from '@grpc/grpc-js' +import { Point } from "./pb/physics_pb.js" +import { GameClient } from './pb/server_grpc_pb' +import { GameSnapshot, JoinRequest, Order, OrderSet, Team } from "./pb/server_pb.js" -import {Bot, PLAYER_STATE} from './stub.js' -import {EnvVarLoader} from './configurator.js' -import {defineState} from './index.js' +import { EnvVarLoader } from './configurator.js' +import GameSnapshotInspector from "./game-snapshot-inspector.js" +import { defineState } from './index' +import { Bot, PLAYER_STATE } from './stub.js' export const PROTOCOL_VERSION = "1.0.0" -export type RawTurnProcessor = (OrderSet, GameSnapshot) => Promise +export type RawTurnProcessorReturn = Order[] | { orders: Order[], debug_message: string } | null; +export type RawTurnProcessor = (inspector: GameSnapshotInspector) => Promise /** * @@ -43,9 +45,9 @@ export class Client { private client: GameClient; /** - * @type {function(GameSnapshot)} + * @type {function(GameSnapshotInspector)} */ - private gettingReadyHandler = function (gs: GameSnapshot) { + private gettingReadyHandler = function (gs: GameSnapshotInspector) { } /** @@ -78,25 +80,25 @@ export class Client { }) { return this.setGettingReadyHandler(s => { bot.gettingReady(s) - })._start((ordersSet, snapshot): Promise => { + })._start((inspector: GameSnapshotInspector): Promise => { return new Promise((resolve, reject) => { - const playerState = defineState(snapshot, this.number, this.teamSide) + const playerState = defineState(inspector, this.number, this.teamSide) if (this.number === 1) { - resolve(bot.asGoalkeeper(ordersSet, snapshot, playerState)); + resolve(bot.asGoalkeeper(inspector, playerState)); return } switch (playerState) { case PLAYER_STATE.DISPUTING_THE_BALL: - resolve(bot.onDisputing(ordersSet, snapshot)); + resolve(bot.onDisputing(inspector)); break; case PLAYER_STATE.DEFENDING: - resolve(bot.onDefending(ordersSet, snapshot)); + resolve(bot.onDefending(inspector)); break; case PLAYER_STATE.SUPPORTING: - resolve(bot.onSupporting(ordersSet, snapshot)); + resolve(bot.onSupporting(inspector)); break; case PLAYER_STATE.HOLDING_THE_BALL: - resolve(bot.onHolding(ordersSet, snapshot)); + resolve(bot.onHolding(inspector)); break; } }); @@ -115,11 +117,11 @@ export class Client { /** * - * @param {function(GameSnapshot)} handler + * @param {function(GameSnapshotInspector)} handler * * @returns {Client} */ - setGettingReadyHandler(handler) { + setGettingReadyHandler(handler: (s: GameSnapshotInspector) => void) { this.gettingReadyHandler = handler return this } @@ -154,14 +156,26 @@ export class Client { const running = this.client.joinATeam(req) onJoin() - running.on('data', async (snapshot) => { + running.on('data', async (snapshot: GameSnapshot) => { try { + const inspector = new GameSnapshotInspector(this.teamSide, this.number, snapshot); switch (snapshot.getState()) { case GameSnapshot.State.LISTENING: let orderSet = new OrderSet() orderSet.setTurn(snapshot.getTurn()) try { - orderSet = await processor(orderSet, snapshot) + const botReturn = await processor(inspector); + + if(!botReturn) { + orderSet.setOrdersList([]); + } + + if(Array.isArray(botReturn)) { + orderSet.setOrdersList(botReturn); + } else { + orderSet.setOrdersList(botReturn.orders); + orderSet.setDebugMessage(botReturn.debug_message); + } } catch (e) { console.error(`bot error`, e.message) } @@ -172,7 +186,7 @@ export class Client { } break; case GameSnapshot.State.GET_READY: - this.gettingReadyHandler(snapshot) + this.gettingReadyHandler(inspector) break; } diff --git a/src/game-snapshot-inspector.ts b/src/game-snapshot-inspector.ts new file mode 100644 index 0000000..84b4cb7 --- /dev/null +++ b/src/game-snapshot-inspector.ts @@ -0,0 +1,216 @@ +import { DIRECTION, Goal } from '.'; +import * as Geo from './geo'; +import { AWAY_GOAL, HOME_GOAL } from './goal'; +import * as Helpers from "./helpers"; +import * as ORIENTATION from './orentation'; +import * as Lugo from './proto_exported'; +import { SPECS } from "./specs"; + +export default class GameSnapshotInspector { + mySide: Lugo.Team.Side; + myNumber: number; + me: Lugo.Player; + snapshot: Lugo.GameSnapshot; + + constructor(botSide: Lugo.Team.Side, playerNumber: number, gameSnapshot: Lugo.GameSnapshot) { + this.mySide = botSide; + this.myNumber = playerNumber; + this.snapshot = gameSnapshot; + + this.me = this.getPlayer(botSide, playerNumber); + if (!this.me) { + throw new Error(`Could not find the player ${botSide}-${playerNumber}`); + } + } + + getSnapshot(): Lugo.GameSnapshot | null { + return this.snapshot; + } + + getTurn() : number { + return this.snapshot.getTurn(); + } + + getMe(): Lugo.Player { + return this.me; + } + + getBall(): Lugo.Ball | null { + return this.snapshot?.getBall() ?? null; + } + + getPlayer(side: Lugo.Team.Side, number: number): Lugo.Player | null { + return Helpers.getPlayer(this.snapshot, side, number); + } + + getBallHolder(): Lugo.Player | null { + return Helpers.getBallHolder(this.snapshot); + } + + isBallHolder(player: Lugo.Player): boolean { + return Helpers.isBallHolder(this.snapshot, player); + } + + getTeam(side: Lugo.Team.Side): Lugo.Team | null { + return Helpers.getTeam(this.snapshot, side); + } + + getMyTeam(): Lugo.Team | null { + return this.getTeam(this.mySide); + } + + getOpponentTeam(): Lugo.Team | null { + return this.getTeam(this.getOpponentSide()); + } + + getMyTeamSide(): Lugo.Team.Side { + return this.mySide; + } + + getOpponentSide(): Lugo.Team.Side { + return Helpers.getOpponentSide(this.mySide); + } + + getMyTeamPlayers(): Lugo.Player[] { + const myTeam = this.getMyTeam(); + return myTeam ? myTeam.getPlayersList() : []; + } + + getOpponentPlayers(): Lugo.Player[] { + const opponentTeam = this.getOpponentTeam(); + return opponentTeam ? opponentTeam.getPlayersList() : []; + } + + getMyTeamGoalkeeper(): Lugo.Player | null { + return this.getPlayer(this.getMyTeamSide(), SPECS.GOALKEEPER_NUMBER); + } + + getOpponentGoalkeeper(): Lugo.Player | null { + return this.getPlayer(this.getOpponentSide(), SPECS.GOALKEEPER_NUMBER); + } + + makeOrderMove(target: Lugo.Point, speed: number): Lugo.Order { + return this.makeOrderMoveFromPoint(this.me?.getPosition() ?? Geo.newZeroedPoint(), target, speed); + } + + makeOrderMoveMaxSpeed(target: Lugo.Point): Lugo.Order { + return this.makeOrderMoveFromPoint(this.me?.getPosition() ?? Geo.newZeroedPoint(), target, SPECS.PLAYER_MAX_SPEED); + } + + makeOrderMoveFromPoint(origin: Lugo.Point, target: Lugo.Point, speed: number): Lugo.Order { + const vec: Lugo.Vector = Geo.NewVector(origin, target); + const vel: Lugo.Velocity = Geo.NewZeroedVelocity(Geo.normalize(vec)); + vel.setSpeed(speed); + const moveOrder = new Lugo.Move() + moveOrder.setVelocity(vel) + return new Lugo.Order().setMove(moveOrder); + } + + makeOrderMoveFromVector(direction: Lugo.Vector, speed: number): Lugo.Order { + const targetPoint: Lugo.Point = Geo.TargetFrom(direction, this.me?.getPosition() ?? Geo.newZeroedPoint()); + return this.makeOrderMoveFromPoint(this.me?.getPosition() ?? Geo.newZeroedPoint(), targetPoint, speed); + } + + makeOrderMoveByDirection(direction: DIRECTION, speed?: number): Lugo.Order { + const directionTarget = this.getOrientationByDirection(direction); + return this.makeOrderMoveFromVector(directionTarget, speed ?? SPECS.PLAYER_MAX_SPEED); + } + + makeOrderMoveToStop(): Lugo.Order { + const myDirection: Lugo.Vector | null = this.getMe()?.getVelocity()?.getDirection() ?? this.getOrientationByDirection(DIRECTION.FORWARD); + return this.makeOrderMoveFromVector(myDirection, 0); + } + + makeOrderJump(target: Lugo.Point, speed: number): Lugo.Order { + const vec: Lugo.Vector = Geo.NewVector(this.me?.getPosition() ?? Geo.newZeroedPoint(), target); + const vel: Lugo.Velocity = Geo.NewZeroedVelocity(Geo.normalize(vec)); + vel.setSpeed(speed); + + const jump = new Lugo.Jump(); + jump.setVelocity(vel); + + return new Lugo.Order().setJump(jump); + } + + makeOrderKick(target: Lugo.Point, speed: number): Lugo.Order { + const ballExpectedDirection: Lugo.Vector = Geo.NewVector(this.snapshot?.getBall()?.getPosition() ?? Geo.newZeroedPoint(), target); + const diffVector: Lugo.Vector = Geo.subVector(ballExpectedDirection, this.snapshot?.getBall()?.getVelocity()?.getDirection() ?? Geo.newZeroedPoint()); + const vel: Lugo.Velocity = Geo.NewZeroedVelocity(Geo.normalize(diffVector)); + vel.setSpeed(speed); + + const kick = new Lugo.Kick(); + kick.setVelocity(vel); + + return new Lugo.Order().setKick(kick); + } + + makeOrderKickMaxSpeed(target: Lugo.Point): Lugo.Order { + return this.makeOrderKick(target, SPECS.BALL_MAX_SPEED); + } + + makeOrderCatch(): Lugo.Order { + const catchOrder = new Lugo.Catch(); + return new Lugo.Order().setCatch(catchOrder); + } + + + private getOrientationByDirection(direction: DIRECTION) { + let directionTarget : Lugo.Vector; + switch (direction) { + case DIRECTION.FORWARD: + directionTarget = ORIENTATION.EAST + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.WEST + } + break; + case DIRECTION.BACKWARD: + directionTarget = ORIENTATION.WEST + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.EAST + } + break; + case DIRECTION.LEFT: + directionTarget = ORIENTATION.NORTH + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.SOUTH + } + break; + case DIRECTION.RIGHT: + directionTarget = ORIENTATION.SOUTH + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.NORTH + } + break; + case DIRECTION.BACKWARD_LEFT: + directionTarget = ORIENTATION.NORTH_WEST + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.SOUTH_EAST + } + break; + case DIRECTION.BACKWARD_RIGHT: + directionTarget = ORIENTATION.SOUTH_WEST + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.NORTH_EAST + } + break; + case DIRECTION.FORWARD_LEFT: + directionTarget = ORIENTATION.NORTH_EAST + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.SOUTH_WEST + } + break; + case DIRECTION.FORWARD_RIGHT: + directionTarget = ORIENTATION.SOUTH_EAST + if (this.mySide === Lugo.Team.Side.AWAY) { + directionTarget = ORIENTATION.NORTH_WEST + } + break; + default: + throw new Error(`unknown direction ${direction}`) + } + return directionTarget; + } +} + + + diff --git a/src/geo.ts b/src/geo.ts index e4f426b..c6f414c 100644 --- a/src/geo.ts +++ b/src/geo.ts @@ -1,4 +1,4 @@ -import {Point, Vector} from './pb/physics_pb.js' +import { Point, Vector, Velocity } from './pb/physics_pb.js'; /** * @@ -82,3 +82,22 @@ function isInValidateVector(v: Vector): boolean { export function distanceBetweenPoints(a: Point, b: Point) : number { return Math.hypot(a.getX() - b.getX(), a.getY() - b.getY()) } + +export function NewZeroedVelocity(direction: Vector): Velocity { + const velocity = new Velocity(); + velocity.setDirection(direction); + velocity.setSpeed(0); + return velocity +} + +export function TargetFrom(v: Vector, point: Point): Point { + const target = new Point(); + target.setX(point.getX() + Math.round(v.getX())); + target.setX(point.getY() + Math.round(v.getY())); + + return target; +} + +export function newZeroedPoint() : Point { + return new Point().setX(0).setY(0); +} \ No newline at end of file diff --git a/src/goal.ts b/src/goal.ts index d0e7542..3241f43 100644 --- a/src/goal.ts +++ b/src/goal.ts @@ -1,5 +1,36 @@ -import {Point} from "./pb/physics_pb"; -import {Team} from "./pb/server_pb"; +import { Point } from "./pb/physics_pb"; +import { Team } from "./pb/server_pb"; +import { SPECS } from "./specs"; + + +const homeGoalCenter = new Point() +homeGoalCenter.setX(0) +homeGoalCenter.setY(SPECS.MAX_Y_COORDINATE / 2) + +const homeGoalTopPole = new Point() +homeGoalTopPole.setX(0) +homeGoalTopPole.setY(SPECS.GOAL_MAX_Y) + +const homeGoalBottomPole = new Point() +homeGoalBottomPole.setX(0) +homeGoalBottomPole.setY(SPECS.GOAL_MIN_Y) + + +const awayGoalCenter = new Point() +awayGoalCenter.setX(SPECS.MAX_X_COORDINATE) +awayGoalCenter.setY(SPECS.MAX_Y_COORDINATE / 2) + + +const awayGoalTopPole = new Point() +awayGoalTopPole.setX(SPECS.MAX_X_COORDINATE) +awayGoalTopPole.setY(SPECS.GOAL_MAX_Y) + +const awayGoalBottomPole = new Point() +awayGoalBottomPole.setX(SPECS.MAX_X_COORDINATE) +awayGoalBottomPole.setY(SPECS.GOAL_MIN_Y) + + + export class Goal { private readonly _center: Point; @@ -38,3 +69,16 @@ export class Goal { } } + +export const AWAY_GOAL = new Goal( + Team.Side.AWAY, + awayGoalCenter, + awayGoalTopPole, + awayGoalBottomPole +) +export const HOME_GOAL = new Goal( + Team.Side.HOME, + homeGoalCenter, + homeGoalTopPole, + homeGoalBottomPole +) diff --git a/src/helpers.ts b/src/helpers.ts new file mode 100644 index 0000000..e7257f9 --- /dev/null +++ b/src/helpers.ts @@ -0,0 +1,37 @@ +import { GameSnapshot, Player, Team } from "./proto_exported"; + +export function getBallHolder (snapshot: GameSnapshot): Player | null { + const holder = snapshot.getBall().getHolder(); + return holder ?? null; +} + +export function isBallHolder (snapshot: GameSnapshot, player: Player): boolean { + const holder = snapshot.getBall().getHolder(); + return holder !== undefined && holder.getTeamSide() === player.getTeamSide() && holder.getNumber() === player.getNumber(); +} + +export function getTeam (snapshot: GameSnapshot, side: Team.Side): Team | null { + if (side === Team.Side.HOME) { + return snapshot.getHomeTeam() ?? null; + } + return snapshot.getAwayTeam() ?? null; +} + +export function getPlayer(snapshot: GameSnapshot, side: Team.Side, number: number): Player | null { + const team = getTeam(snapshot, side); + if (team) { + let player = null ; + + for (const currentPlayer of team.getPlayersList()) { + if(currentPlayer.getNumber() === number) player = currentPlayer; + } + + return player; + } + return null; +} + +export function getOpponentSide(side: Team.Side): Team.Side { + return side === Team.Side.HOME ? Team.Side.AWAY : Team.Side.HOME; +} + diff --git a/src/index.ts b/src/index.ts index 76c40d4..aaac3e2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,343 +1,24 @@ -import {Client, NewClientFromConfig} from './client' -import {EnvVarLoader} from './configurator' -import {Goal} from './goal' -import {Mapper, Region} from './mapper' +import { Client, NewClientFromConfig } from './client' +import { EnvVarLoader } from './configurator' +import GameSnapshotInspector from './game-snapshot-inspector' +import * as geo from "./geo" +import { NewVector, distanceBetweenPoints, getLength, getScaledVector, normalize, subVector } from "./geo" +import { Goal } from './goal' +import { Mapper, Region } from './mapper' import * as ORIENTATION from './orentation' import * as Lugo from './proto_exported' -import {SPECS} from "./specs.js" -import {Bot, PLAYER_STATE} from './stub' -import * as geo from "./geo" -import {normalize, distanceBetweenPoints, getLength, subVector, getScaledVector, NewVector} from "./geo" import * as rl from "./rl/index" +import { SPECS } from "./specs.js" +import { Bot, PLAYER_STATE } from './stub' +import { NewDefaultStarter } from './starter' +import { DefaultInitBundle } from './util/defaults' // imports actually used in this file export { - rl, - Client, NewClientFromConfig, - EnvVarLoader, - Goal, - Mapper, Region, - ORIENTATION, - SPECS, - Bot, PLAYER_STATE, - Lugo, - geo,// keeping backward compatibility - normalize, distanceBetweenPoints, getLength, subVector, getScaledVector, NewVector, -} - - -const homeGoalCenter = new Lugo.Point() -homeGoalCenter.setX(0) -homeGoalCenter.setY(SPECS.MAX_Y_COORDINATE / 2) - -const homeGoalTopPole = new Lugo.Point() -homeGoalTopPole.setX(0) -homeGoalTopPole.setY(SPECS.GOAL_MAX_Y) - -const homeGoalBottomPole = new Lugo.Point() -homeGoalBottomPole.setX(0) -homeGoalBottomPole.setY(SPECS.GOAL_MIN_Y) - - -const awayGoalCenter = new Lugo.Point() -awayGoalCenter.setX(SPECS.MAX_X_COORDINATE) -awayGoalCenter.setY(SPECS.MAX_Y_COORDINATE / 2) - - -const awayGoalTopPole = new Lugo.Point() -awayGoalTopPole.setX(SPECS.MAX_X_COORDINATE) -awayGoalTopPole.setY(SPECS.GOAL_MAX_Y) - -const awayGoalBottomPole = new Lugo.Point() -awayGoalBottomPole.setX(SPECS.MAX_X_COORDINATE) -awayGoalBottomPole.setY(SPECS.GOAL_MIN_Y) - - -export class GameSnapshotReader { - readonly mySide; - - /** - * @type {Lugo.GameSnapshot} - */ - readonly snapshot; - - constructor(snapshot: Lugo.GameSnapshot, mySide: Lugo.Team.Side) { - this.snapshot = snapshot - this.mySide = mySide - } - - /** - * Returns the bot team - * @returns {Lugo.Team} - */ - getMyTeam(): Lugo.Team { - return this.getTeam(this.mySide) - } - - /** - * Returns the opponent team - * @returns {Lugo.Team} - */ - getOpponentTeam(): Lugo.Team { - return this.getTeam(this.getOpponentSide()) - } - - /** - * @param { Lugo.Team.Side} side - * @returns {Lugo.Team} - */ - getTeam(side): Lugo.Team { - if (side === Lugo.Side.HOME) { - return this.snapshot.getHomeTeam() - } - return this.snapshot.getAwayTeam() - } - - - /** - * - * @param { Player} player - * @returns {boolean} - */ - isBallHolder(player: Lugo.Player): boolean { - const ball = this.snapshot.getBall() - - return ball.getHolder() != null && ball.getHolder().getTeamSide() === player.getTeamSide() && ball.getHolder().getNumber() === player.getNumber() - } - - /** - * - * @returns {Lugo.Team.Side} - */ - getOpponentSide(): Lugo.Team.Side { - if (this.mySide === Lugo.Team.Side.HOME) { - return Lugo.Team.Side.AWAY - } - return Lugo.Team.Side.HOME - } - - /** - * - * @returns {Goal} - */ - getMyGoal(): Goal { - if (this.mySide === Lugo.Team.Side.HOME) { - return homeGoal - } - return awayGoal - } - - /** - * - * @returns {Lugo.Ball} - */ - getBall(): Lugo.Ball { - return this.snapshot.getBall() - } - - /** - * - * @returns {Goal} - */ - getOpponentGoal(): Goal { - if (this.mySide === Lugo.Team.Side.HOME) { - return awayGoal - } - return homeGoal - } - - /** - * - * @param {.Lugo.Team.Side} side - * @param {number} number - * @returns {.Player} - */ - getPlayer(side: Lugo.Team.Side, number: number): Lugo.Player | null { - const team = this.getTeam(side) - if (team == null) { - return null - } - for (const player of team.getPlayersList()) { - if (player.getNumber() === number) { - return player - } - } - return null - } - - /** - * - * @param {Point} origin - * @param {Point} target - * @return {Order} - */ - makeOrderMoveMaxSpeed(origin: Lugo.Point, target: Lugo.Point): Lugo.Order { - return this.makeOrderMove(origin, target, SPECS.PLAYER_MAX_SPEED) - } - - /** - * - * @param {Point} origin - * @param {Point} target - * @param speed - * @returns {Order} - */ - makeOrderMove(origin: Lugo.Point, target: Lugo.Point, speed: number): Lugo.Order { - if (origin.getX() === target.getX() && origin.getY() === target.getY()) { - // a vector cannot have zeroed direction. In this case, the player will just be stopped - return this.makeOrderMoveFromVector(ORIENTATION.NORTH, 0) - } - - let direction = geo.NewVector(origin, target) - direction = geo.normalize(direction) - return this.makeOrderMoveFromVector(direction, speed) - } - - /** - * - * @param {Vector} direction - * @param {number} speed - * @returns {Order} - * @private - */ - makeOrderMoveFromVector(direction: Lugo.Vector, speed: number): Lugo.Order { - const velocity = new Lugo.Velocity() - velocity.setDirection(direction) - velocity.setSpeed(speed) - - const moveOrder = new Lugo.Move() - moveOrder.setVelocity(velocity) - return new Lugo.Order().setMove(moveOrder) - } - - makeOrderMoveByDirection(direction: DIRECTION): Lugo.Order { - let directionTarget; - switch (direction) { - case DIRECTION.FORWARD: - directionTarget = ORIENTATION.EAST - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.WEST - } - break; - case DIRECTION.BACKWARD: - directionTarget = ORIENTATION.WEST - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.EAST - } - break; - case DIRECTION.LEFT: - directionTarget = ORIENTATION.NORTH - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.SOUTH - } - break; - case DIRECTION.RIGHT: - directionTarget = ORIENTATION.SOUTH - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.NORTH - } - break; - case DIRECTION.BACKWARD_LEFT: - directionTarget = ORIENTATION.NORTH_WEST - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.SOUTH_EAST - } - break; - case DIRECTION.BACKWARD_RIGHT: - directionTarget = ORIENTATION.SOUTH_WEST - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.NORTH_EAST - } - break; - case DIRECTION.FORWARD_LEFT: - directionTarget = ORIENTATION.NORTH_EAST - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.SOUTH_WEST - } - break; - case DIRECTION.FORWARD_RIGHT: - directionTarget = ORIENTATION.SOUTH_EAST - if (this.mySide === Lugo.Team.Side.AWAY) { - directionTarget = ORIENTATION.NORTH_WEST - } - break; - default: - throw new Error(`unknown direction ${direction}`) - - } - return this.makeOrderMoveFromVector(directionTarget, SPECS.PLAYER_MAX_SPEED) - } - - - makeOrderJump(origin: Lugo.Point, target: Lugo.Point, speed: number): Lugo.Order { - let direction = ORIENTATION.EAST - if (origin.getX() !== target.getX() || origin.getY() !== target.getY()) { - // a vector cannot have zeroed direction. In this case, the player will just be stopped - direction = geo.NewVector(origin, target) - direction = geo.normalize(direction) - } - const velocity = new Lugo.Velocity() - velocity.setDirection(direction) - velocity.setSpeed(speed) - - const jump = new Lugo.Jump() - jump.setVelocity(velocity) - - return new Lugo.Order().setJump(jump) - } - - /** - * - * @param {Ball} ball - * @param {Point} target - * @param {number} speed - * @returns {Order} - */ - makeOrderKick(ball: Lugo.Ball, target: Lugo.Point, speed: number): Lugo.Order { - const ballExpectedDirection = geo.NewVector(ball.getPosition(), target) - - // the ball velocity is summed to the kick velocity, so we have to consider the current ball direction - const diffVector = geo.subVector(ballExpectedDirection, ball.getVelocity().getDirection()) - - const newVelocity = new Lugo.Velocity() - newVelocity.setSpeed(speed) - newVelocity.setDirection(geo.normalize(diffVector)) - return new Lugo.Order().setKick(new Lugo.Kick().setVelocity(newVelocity)) - } - - /** - * - * @param {Ball} ball - * @param {Point} target - * @returns {Order} - */ - makeOrderKickMaxSpeed(ball: Lugo.Ball, target: Lugo.Point): Lugo.Order { - return this.makeOrderKick(ball, target, SPECS.BALL_MAX_SPEED) - } - - /** - * - * @returns {!Order} - */ - makeOrderCatch(): Lugo.Order { - return new Lugo.Order().setCatch(new Lugo.Catch()) - } - + Bot, Client, EnvVarLoader, GameSnapshotInspector, Goal, Lugo, Mapper, NewClientFromConfig, NewVector, ORIENTATION, PLAYER_STATE, Region, SPECS, distanceBetweenPoints, geo, getLength, getScaledVector, // keeping backward compatibility + normalize, rl, subVector, NewDefaultStarter, DefaultInitBundle } -export const awayGoal = new Goal( - Lugo.Team.Side.AWAY, - awayGoalCenter, - awayGoalTopPole, - awayGoalBottomPole -) -export const homeGoal = new Goal( - Lugo.Team.Side.HOME, - homeGoalCenter, - homeGoalTopPole, - homeGoalBottomPole -) - export enum DIRECTION { FORWARD, BACKWARD, @@ -352,18 +33,17 @@ export enum DIRECTION { /** * - * @param {GameSnapshot} snapshot + * @param {GameSnapshotInspector} snapshot * @param playerNumber * @param side * @returns {PLAYER_STATE} */ -export function defineState(snapshot: Lugo.GameSnapshot, playerNumber: number, side: Lugo.Team.Side): PLAYER_STATE { +export function defineState(snapshot: GameSnapshotInspector, playerNumber: number, side: Lugo.Team.Side): PLAYER_STATE { if (!snapshot || !snapshot.getBall()) { throw new Error('invalid snapshot state - cannot define player state') } - const reader = new GameSnapshotReader(snapshot, side) - const me = reader.getPlayer(side, playerNumber) + const me = snapshot.getPlayer(side, playerNumber) if (!me) { throw new Error('could not find the bot in the snapshot - cannot define player state') } diff --git a/src/mapper.ts b/src/mapper.ts index 55ee6b7..d9b3d97 100644 --- a/src/mapper.ts +++ b/src/mapper.ts @@ -1,3 +1,4 @@ +import { AWAY_GOAL, Goal, HOME_GOAL } from './goal.js' import * as physics from './pb/physics_pb.js' import * as lugo from './pb/server_pb.js' @@ -182,6 +183,21 @@ export class Mapper { this.regionHeight = SPECS.MAX_Y_COORDINATE / rows } + + getDefenseGoal(): Goal { + if (this.side === lugo.Team.Side.HOME) { + return HOME_GOAL + } + return AWAY_GOAL + } + + getAttackGoal(): Goal { + if (this.side === lugo.Team.Side.AWAY) { + return AWAY_GOAL + } + return HOME_GOAL + } + /** * @param col {number} * @param row {number} diff --git a/src/rl/gym.ts b/src/rl/gym.ts index 5741e98..f791954 100644 --- a/src/rl/gym.ts +++ b/src/rl/gym.ts @@ -1,14 +1,14 @@ -import {delay, TrainingCrl} from "./trainingCrl"; +import { Client } from '../client'; +import GameSnapshotInspector from "../game-snapshot-inspector"; +import { Order, Team } from "../pb/server_pb.js"; import { newChaserHelperPlayer, - newCustomHelperPlayer, newRandomMotionHelperPlayer, newZombieHelperPlayer } from "./helper_bots"; -import {RemoteControl} from "./remoteControl" -import {BotTrainer, TrainingFunction} from "./interfaces"; -import {Client, RawTurnProcessor} from '../client' -import {OrderSet, Team} from "../pb/server_pb.js" +import { BotTrainer, TrainingFunction } from "./interfaces"; +import { RemoteControl } from "./remoteControl"; +import { delay, TrainingCrl } from "./trainingCrl"; export class Gym { @@ -30,9 +30,9 @@ export class Gym { // If the game was started in a previous training session, the game server will be stuck on the listening phase. // so we check if the game has started, if now, we try to resume the server let hasStarted = false; - await lugoClient.play((orderSet, snapshot): Promise => { + await lugoClient.play((snapshot: GameSnapshotInspector): Promise => { hasStarted = true; - return this.trainingCrl.gameTurnHandler(orderSet, snapshot) + return this.trainingCrl.gameTurnHandler(snapshot) }, async () => { if (this.gameServerAddress) { await this.helperPlayers(this.gameServerAddress) diff --git a/src/rl/helper_bots.ts b/src/rl/helper_bots.ts index 7b39ae6..9fd8918 100644 --- a/src/rl/helper_bots.ts +++ b/src/rl/helper_bots.ts @@ -1,7 +1,7 @@ -import {Mapper} from '../mapper'; -import {Client, RawTurnProcessor} from '../client' -import {GameSnapshot, OrderSet} from "../pb/server_pb"; -import {DIRECTION, GameSnapshotReader} from "../index"; +import { Client, RawTurnProcessor, type RawTurnProcessorReturn } from '../client'; +import GameSnapshotInspector from '../game-snapshot-inspector'; +import { DIRECTION } from "../index"; +import { Mapper } from '../mapper'; const PLAYER_POSITIONS = { 1: {Col: 0, Row: 1}, @@ -22,7 +22,7 @@ const PLAYER_POSITIONS = { export function newRandomMotionHelperPlayer(teamSide, playerNumber, gameServerAddress, turnsToChangeDirection = 60) { let changeCounter = 0 let currentDir; - return newCustomHelperPlayer(teamSide, playerNumber, gameServerAddress, async (orderSet: OrderSet, snapshot: GameSnapshot): Promise => { + return newCustomHelperPlayer(teamSide, playerNumber, gameServerAddress, async (snapshot: GameSnapshotInspector): Promise => { changeCounter--; if (changeCounter <= 0) { console.log("change dir!", teamSide, playerNumber, snapshot.getTurn()) @@ -38,32 +38,36 @@ export function newRandomMotionHelperPlayer(teamSide, playerNumber, gameServerAd DIRECTION.FORWARD_LEFT, ][Math.floor(Math.random() * 8)] } - const reader = new GameSnapshotReader(snapshot, teamSide) - orderSet.addOrders(reader.makeOrderMoveByDirection(currentDir)) - orderSet.setDebugMessage(`${teamSide === 0 ? 'HOME' : 'AWAY'}-${playerNumber} #${snapshot.getTurn()} - chasing ball`) - return orderSet; + + const orders = [snapshot.makeOrderMoveByDirection(currentDir)]; + const debug_message = `${teamSide === 0 ? 'HOME' : 'AWAY'}-${playerNumber} #${snapshot.getTurn()} - chasing ball`; + return { orders, debug_message }; }) } export function newChaserHelperPlayer(teamSide, playerNumber, gameServerAddress) { - return newCustomHelperPlayer(teamSide, playerNumber, gameServerAddress, async (orderSet: OrderSet, snapshot: GameSnapshot): Promise => { - const reader = new GameSnapshotReader(snapshot, teamSide) - orderSet.addOrders(reader.makeOrderCatch()) - const me = reader.getPlayer(teamSide, playerNumber) + return newCustomHelperPlayer(teamSide, playerNumber, gameServerAddress, async (snapshot: GameSnapshotInspector): Promise => { + const orders = []; + + orders.push(snapshot.makeOrderCatch()) + const me = snapshot.getPlayer(teamSide, playerNumber); if (!me) { throw new Error("did not find myself in the game") } - orderSet.addOrders(reader.makeOrderMoveMaxSpeed(me.getPosition(), snapshot.getBall().getPosition())) - orderSet.setDebugMessage(`${teamSide === 0 ? 'HOME' : 'AWAY'}-${playerNumber} #${snapshot.getTurn()} - chasing ball`) - return orderSet; + + + orders.push(snapshot.makeOrderMoveMaxSpeed(snapshot.getBall().getPosition())); + const debug_message = `${teamSide === 0 ? 'HOME' : 'AWAY'}-${playerNumber} #${snapshot.getTurn()} - chasing ball`; + return { orders, debug_message }; }) } export function newZombieHelperPlayer(teamSide, playerNumber, gameServerAddress): Promise { - return newCustomHelperPlayer(teamSide, playerNumber, gameServerAddress, async (orderSet: OrderSet, snapshot: GameSnapshot): Promise => { - orderSet.setDebugMessage(`${teamSide === 0 ? 'HOME' : 'AWAY'}-${playerNumber} #${snapshot.getTurn()}`) - return orderSet; + return newCustomHelperPlayer(teamSide, playerNumber, gameServerAddress, async (snapshot: GameSnapshotInspector): Promise => { + const orders = []; + const debug_message = `${teamSide === 0 ? 'HOME' : 'AWAY'}-${playerNumber} #${snapshot.getTurn()}`; + return { orders, debug_message }; }) } diff --git a/src/rl/trainingCrl.ts b/src/rl/trainingCrl.ts index 070d6e4..671846b 100644 --- a/src/rl/trainingCrl.ts +++ b/src/rl/trainingCrl.ts @@ -1,6 +1,7 @@ -import {RemoteControl} from "./remoteControl" -import {BotTrainer, TrainingController, TrainingFunction} from './interfaces' -import {GameSnapshot, OrderSet} from "../pb/server_pb" +import GameSnapshotInspector from "../game-snapshot-inspector"; +import { GameSnapshot, Order, OrderSet } from "../pb/server_pb"; +import { BotTrainer, TrainingController, TrainingFunction } from './interfaces'; +import { RemoteControl } from "./remoteControl"; export const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) @@ -106,7 +107,7 @@ export class TrainingCrl implements TrainingController { this._debug(`No one waiting for the next state`) }; - async gameTurnHandler(orderSet, snapshot): Promise { + async gameTurnHandler(snapshot: GameSnapshotInspector): Promise { /** * This method is called when the game is on the `LISTENING` state. * When the game starts it takes a while to enter this state. @@ -123,14 +124,14 @@ export class TrainingCrl implements TrainingController { if (this.onListeningMode) { throw new Error("faulty synchrony - got new turn while the trainer already was in listening mode (check the lugo 'timer-mode')") } - this._gotNextState(snapshot) + this._gotNextState(snapshot.getSnapshot()) // [explaining flow] this promise will ensure that we will only return the bot order after resumeListeningPhase has been called. return await new Promise(async (resolve, reject) => { // [explaining flow] this setTimeout ensures that we will not wait forever for the bot to return const maxWait = setTimeout(() => { if (this.stopRequested) { - return resolve(orderSet) + return resolve([]) } console.error(`max wait for a new action`) reject() @@ -139,13 +140,13 @@ export class TrainingCrl implements TrainingController { if (this.stopRequested) { this._debug(`stop requested - will not defined call back for new actions`) - resolve(orderSet) + resolve([]) clearTimeout(maxWait) return null } - this.OrderSet = orderSet + this.OrderSet = new OrderSet(); // [explaining flow] here we will define the callback called when the bot returns the orders. diff --git a/src/starter.ts b/src/starter.ts new file mode 100644 index 0000000..6c7379a --- /dev/null +++ b/src/starter.ts @@ -0,0 +1,64 @@ +import { Bot, NewClientFromConfig } from "."; +import { RawTurnProcessor } from "./client"; +import { EnvVarLoader } from "./configurator"; +import { Mapper } from "./mapper"; +import { Point } from "./proto_exported"; +import { DefaultInitBundle } from "./util/defaults"; + + +export function NewDefaultStarter() { + const { defaultConfig, defaultMapper, defaultInitialPosition } = DefaultInitBundle(); + return new Starter(defaultInitialPosition, defaultConfig, defaultMapper); +} + +class Starter { + constructor( + private initial_position: Point, + private config: EnvVarLoader, + private mapper: Mapper, + ) {} + + getMapper(): Mapper { + return this.mapper; + } + + setMapper(mapper: Mapper): void { + this.mapper = mapper; + } + + getInitialPosition(): Point { + return this.initial_position + } + + setInitialPosition(initial_position: Point): void { + this.initial_position = initial_position; + } + + getConfig(): EnvVarLoader { + return this.config; + } + + setConfig(config: EnvVarLoader): void { + this.config = config; + } + + run(bot: Bot, onJoin?: () => void){ + const lugoClient = NewClientFromConfig(this.config, this.initial_position) + + lugoClient.playAsBot(bot, onJoin).then(() => { + console.log(`all done`) + }).catch(e => { + console.error(e) + }) + } + + runJustTurnHandler(raw_processor: RawTurnProcessor, onJoin?: () => void) { + const lugoClient = NewClientFromConfig(this.config, this.initial_position) + + lugoClient.play(raw_processor, onJoin).then(() => { + console.log(`all done`) + }).catch(e => { + console.error(e) + }) + } +} \ No newline at end of file diff --git a/src/stub.ts b/src/stub.ts index 3c7c8f8..7e9ede2 100644 --- a/src/stub.ts +++ b/src/stub.ts @@ -1,4 +1,5 @@ -import {GameSnapshot, OrderSet} from './pb/server_pb.js' +import GameSnapshotInspector from './game-snapshot-inspector.js' +import { Order } from './pb/server_pb.js' export enum PLAYER_STATE { SUPPORTING = "supporting", @@ -7,61 +8,57 @@ export enum PLAYER_STATE { DISPUTING_THE_BALL = "disputing", } + export interface Bot { /** * OnDisputing is called when no one has the ball possession * - * @param {OrderSet} orderSet - * @param {GameSnapshot} snapshot - * @returns {OrderSet | null} + * @param {GameSnapshotInspector} inspector + * @returns {Order[] | { orders: Order[], debug_message: string } | null} */ - onDisputing: (orderSet: OrderSet, snapshot: GameSnapshot) => OrderSet | null + onDisputing: (inspector: GameSnapshotInspector) => Order[] | { orders: Order[], debug_message: string } | null /** * OnDefending is called when an opponent player has the ball possession * - * @param {OrderSet} orderSet - * @param {GameSnapshot} snapshot - * @returns {OrderSet | null} + * @param {GameSnapshotInspector} inspector + * @returns {Order[] | { orders: Order[], debug_message: string } | null} */ - onDefending: (orderSet: OrderSet, snapshot: GameSnapshot) => OrderSet | null + onDefending: (inspector: GameSnapshotInspector) => Order[] | { orders: Order[], debug_message: string } | null /** * OnHolding is called when this bot has the ball possession * - * @param {OrderSet} orderSet - * @param {GameSnapshot} snapshot - * @returns {OrderSet | null} + * @param {GameSnapshotInspector} inspector + * @returns {Order[] | { orders: Order[], debug_message: string } | null} */ - onHolding: (orderSet: OrderSet, snapshot: GameSnapshot) => OrderSet | null + onHolding: (inspector: GameSnapshotInspector) => Order[] | { orders: Order[], debug_message: string } | null /** * OnSupporting is called when a teammate player has the ball possession * - * @param {OrderSet} orderSet - * @param {GameSnapshot} snapshot - * @returns {OrderSet | null} + * @param {GameSnapshotInspector} inspector + * @returns {Order[] | { orders: Order[], debug_message: string } | null} */ - onSupporting: (orderSet: OrderSet, snapshot: GameSnapshot) => OrderSet | null + onSupporting: (inspector: GameSnapshotInspector) => Order[] | { orders: Order[], debug_message: string } | null /** * AsGoalkeeper is only called when this bot is the goalkeeper (number 1). This method is called on every turn, * and the player state is passed at the last parameter. - * @param {OrderSet} orderSet - * @param {GameSnapshot} snapshot + * @param {GameSnapshotInspector} inspector * @param {PLAYER_STATE} state - * @returns {OrderSet | null} + * @returns {Order[] | { orders: Order[], debug_message: string } | null} */ - asGoalkeeper: (orderSet: OrderSet, snapshot: GameSnapshot, state: PLAYER_STATE) => OrderSet | null + asGoalkeeper: (inspector: GameSnapshotInspector, state: PLAYER_STATE) => Order[] | { orders: Order[], debug_message: string } | null /** * gettingReady is called when the game is on Getting Ready state. * - * @param snapshot + * @param {GameSnapshotInspector} inspector */ - gettingReady: (snapshot: GameSnapshot) => void + gettingReady: (inspector: GameSnapshotInspector) => void } diff --git a/src/util/defaults.ts b/src/util/defaults.ts new file mode 100644 index 0000000..945fb57 --- /dev/null +++ b/src/util/defaults.ts @@ -0,0 +1,36 @@ +import { EnvVarLoader } from "../configurator" +import { Mapper } from "../mapper" +import { Point } from "../proto_exported"; + +export function DefaultInitBundle(): { + defaultConfig: EnvVarLoader; + defaultMapper: Mapper; + defaultInitialPosition: Point; +} { + // the map will help us to see the field in quadrants (called regions) instead of working with coordinates + const defaultConfig = new EnvVarLoader() + + // the map will help us to see the field in quadrants (called regions) instead of working with coordinates + const defaultMapper = new Mapper(10, 6, defaultConfig.getBotTeamSide()) + + // our bot strategy defines our bot initial position based on its number + const initialRegion = defaultMapper.getRegion(DEFAULT_PLAYER_POSITIONS[defaultConfig.getBotNumber()].Col, DEFAULT_PLAYER_POSITIONS[defaultConfig.getBotNumber()].Row) + + const defaultInitialPosition = initialRegion.getCenter(); + + return { defaultConfig, defaultMapper, defaultInitialPosition } +} + +export const DEFAULT_PLAYER_POSITIONS = { + 1: {Col: 0, Row: 0}, + 2: {Col: 1, Row: 1}, + 3: {Col: 2, Row: 2}, + 4: {Col: 2, Row: 3}, + 5: {Col: 1, Row: 4}, + 6: {Col: 3, Row: 1}, + 7: {Col: 3, Row: 2}, + 8: {Col: 3, Row: 3}, + 9: {Col: 3, Row: 4}, + 10: {Col: 4, Row: 3}, + 11: {Col: 4, Row: 2}, +} \ No newline at end of file