Skip to content

Commit

Permalink
New features and bug fixes:
Browse files Browse the repository at this point in the history
- added handling of blocked paths
- added following procedure
- solved bug when removing expired parcels

Co-authored-by: Manuela Corte Pause <ManuelaCorte@users.noreply.github.com>
  • Loading branch information
frangente and ManuelaCorte committed Jan 27, 2024
1 parent 9a07853 commit 1ce4710
Show file tree
Hide file tree
Showing 8 changed files with 511 additions and 173 deletions.
61 changes: 55 additions & 6 deletions src/domain/beliefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface TeamMate {
position: Position;
lastHeard: Instant;
intentions: [Intention, number][];
ignore: boolean;
}

export class BeliefSet {
Expand All @@ -50,6 +51,8 @@ export class BeliefSet {

private readonly _broker: EventEmitter = new EventEmitter();

private readonly _ignoredParcels: HashSet<ParcelID> = new HashSet();

public parcelDiscounts: HashMap<ParcelID, number> = new HashMap();

// ------------------------------------------------------------------------
Expand Down Expand Up @@ -113,13 +116,15 @@ export class BeliefSet {
const newFreeParcels: ParcelID[] = [];
const changedPositionParcels: [ParcelID, Position, Position][] = [];
const noLongerFreeParcels: [ParcelID, Position, DecayingValue][] = [];
const expiredParcels: [ParcelID, Position, DecayingValue][] = [];

const { parcelRadius } = Config.getEnvironmentConfig();

const visibleParcelIDs = new HashSet(visibleParcels.map((p) => p.id));
for (const [id, parcel] of this._freeParcels.entries()) {
if (parcel.isExpired()) {
this._removeParcel(id);
expiredParcels.push([id, parcel.position, parcel.value]);
continue;
}

Expand All @@ -136,6 +141,10 @@ export class BeliefSet {
}

for (const parcel of visibleParcels) {
if (this._ignoredParcels.has(parcel.id)) {
continue;
}

if (parcel.agentID === null) {
// the parcel is free
if (this._freeParcels.has(parcel.id)) {
Expand Down Expand Up @@ -178,6 +187,17 @@ export class BeliefSet {
noLongerFreeParcels,
);
}

if (expiredParcels.length > 0) {
this._broker.emit("expired-parcels", expiredParcels);
}
}

public addIgnoredParcels(parcels: ParcelID[]) {
for (const parcelId of parcels) {
this._ignoredParcels.add(parcelId);
this._removeParcel(parcelId);
}
}

// ------------------------------------------------------------------------
Expand Down Expand Up @@ -323,6 +343,7 @@ export class BeliefSet {
position: new Position(0, 0), // Here we can use any position since it will be updated later.
lastHeard: Instant.now(),
intentions: [],
ignore: false,
});
}

Expand Down Expand Up @@ -363,6 +384,16 @@ export class BeliefSet {
teamMate.lastHeard = Instant.now();
}

public updateTeamMateIgnore(agentID: AgentID, ignore: boolean) {
const teamMate = this._teamMates.get(agentID);
if (teamMate === undefined) {
throw new TeamMateNotFoundError();
}

teamMate.ignore = ignore;
teamMate.lastHeard = Instant.now();
}

// ------------------------------------------------------------------------
// Other public methods
// ------------------------------------------------------------------------
Expand Down Expand Up @@ -408,10 +439,13 @@ export class BeliefSet {
}

/**
* Computes the bottleneck between start and end. The bottleneck is the
* set of positions that must necessarily be crossed to go from start to end.
* Computes the bottleneck between start and end.
* The bottleneck is the set of positions that must necessarily be crossed
* to go from start to end.
*
* @param start The starting position.
* @param end The ending position.
*
* @returns The bottleneck between start and end.
*/
public computeBottleneck(start: Position, end: Position): HashSet<Position> {
Expand Down Expand Up @@ -458,8 +492,10 @@ export class BeliefSet {
/**
* Computes the shortest path from start to end taking into account the
* current state of the environment.
*
* @param start The starting position.
* @param end The ending position.
*
* @returns The shortest path from start to end or null if no path exists.
*/
public recomputePath(start: Position, end: Position): Direction[] | null {
Expand Down Expand Up @@ -518,6 +554,11 @@ export class BeliefSet {
// Event listeners
// ------------------------------------------------------------------------

/**
* Registers a callback that is called when the set of free parcels changes.
*
* @param callback The callback to register.
*/
public onParcelsChange(
callback: (
newFreeParcels: ParcelID[],
Expand All @@ -528,6 +569,10 @@ export class BeliefSet {
this._broker.on("parcels-change", callback);
}

public onExpiredParcels(callback: (parcels: [ParcelID, Position, DecayingValue][]) => void) {
this._broker.on("expired-parcels", callback);
}

public onOccupiedPositionsChange(callback: () => void) {
this._broker.on("occupied-positions-change", callback);
}
Expand All @@ -537,16 +582,20 @@ export class BeliefSet {
// ------------------------------------------------------------------------

private _removeDeadParcels() {
const noLongerFreeParcels: [ParcelID, Position, DecayingValue][] = [];
for (const id of this._ignoredParcels.values()) {
this._ignoredParcels.delete(id);
}

const expiredParcels: [ParcelID, Position, DecayingValue][] = [];
for (const [id, parcel] of this._freeParcels.entries()) {
if (parcel.isExpired()) {
noLongerFreeParcels.push([id, parcel.position, parcel.value]);
expiredParcels.push([id, parcel.position, parcel.value]);
this._removeParcel(id);
}
}

if (noLongerFreeParcels.length > 0) {
this._broker.emit("parcels-change", [], [], noLongerFreeParcels);
if (expiredParcels.length > 0) {
this._broker.emit("expired-parcels", expiredParcels);
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/domain/map/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ export class GridMap {
return this._distance(from, to);
}

public distanceIfPossible(from: Position, to: Position): number | null {
if (!this._graph.hasUndirectedEdge(from.hash(), to.hash())) {
return null;
}

return this._distance(from, to);
}

/**
* Returns the length of the shortest path between the given position and the closest delivery position.
*
Expand Down
98 changes: 73 additions & 25 deletions src/domain/planner/mcts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Config,
DecayingValue,
Intention,
IntentionType,
Parcel,
ParcelID,
Position,
Expand All @@ -33,6 +34,7 @@ export class MonteCarloTreeSearch {
this._beliefs = enviroment;

this._beliefs.onParcelsChange(this._onParcelsChange.bind(this));
this._beliefs.onExpiredParcels(this._onExpiredParcels.bind(this));
}

// ------------------------------------------------------------------------
Expand Down Expand Up @@ -149,38 +151,51 @@ export class MonteCarloTreeSearch {
]);
}

public printTree(instant: Instant, position: Position) {
public addAllPutdownIntentions() {
if (this._root === null) {
throw new MCTSNotStartedError();
}

console.log("Tree:");
console.log(
treefy.asTree(this._getTree(this._root.children, instant, position), true, false),
);
const alreadyAdded = new Set<Intention>();
for (const intention of this._root.nextIntentions) {
if (intention.type === IntentionType.PUTDOWN) {
alreadyAdded.add(intention);
}
}

const newIntentions: Intention[] = [];
for (const delivery of this._beliefs.map.deliveryTiles) {
const intention = Intention.putdown(delivery.position);
if (!alreadyAdded.has(intention)) {
newIntentions.push(intention);
}
}

this._root.addIntentions(newIntentions);
}

private _getTree(children: Node[], startTime: Instant, position: Position): any {
const res: any = {};
for (const [idx, node] of children.entries()) {
const distance = this._beliefs.map.distance(
position,
node.state.executedIntenion.position,
);
const { movementDuration } = Config.getEnvironmentConfig();
const arrivalTime = startTime.add(movementDuration.multiply(distance));
public removeCarryingParcels(): ParcelID[] {
if (this._root === null) {
throw new MCTSNotStartedError();
}

const parcels = Array.from(node.utility.parcels.keys());
res[`child_${idx}`] = {
intention: node.state.executedIntenion,
visits: node.visits,
utility_parcels: parcels,
score: node.utility.getValueByInstant(arrivalTime) / node.visits,
...this._getTree(node.children, arrivalTime, node.state.executedIntenion.position),
};
const parcels = this._root.state.pickedParcels;
for (const [id, parcel] of parcels) {
this._root.partialRemoveParcel(id, parcel);
}

return res;
return parcels.map(([id]) => id);
}

public printTree(instant: Instant, position: Position) {
if (this._root === null) {
throw new MCTSNotStartedError();
}

console.log("Tree:");
console.log(
treefy.asTree(this._getTree(this._root.children, instant, position), true, false),
);
}

// ------------------------------------------------------------------------
Expand All @@ -198,14 +213,14 @@ export class MonteCarloTreeSearch {

if (noLongerFreeParcels.length > 0) {
for (const [id, pos, value] of noLongerFreeParcels) {
this._root.removeParcel(id, pos, value);
this._root.removeNoLongerFreeParcel(id, pos, value);
}
}

if (changedPositionParcels.length > 0) {
for (const [id, oldPos, newPos] of changedPositionParcels) {
const value = this._beliefs.getParcelByID(id)!.value;
this._root.removeParcel(id, oldPos, value);
this._root.removeNoLongerFreeParcel(id, oldPos, value);
newFreeParcels.push(id);
}
}
Expand All @@ -214,4 +229,37 @@ export class MonteCarloTreeSearch {
this._root.addNewFreeParcels(newFreeParcels);
}
}

private _onExpiredParcels(parcels: [ParcelID, Position, DecayingValue][]) {
if (this._root === null) {
return;
}

for (const [id, pos, value] of parcels) {
this._root.removeExpiredParcel(id, pos, value);
}
}

private _getTree(children: Node[], startTime: Instant, position: Position): any {
const res: any = {};
for (const [idx, node] of children.entries()) {
const distance = this._beliefs.map.distance(
position,
node.state.executedIntenion.position,
);
const { movementDuration } = Config.getEnvironmentConfig();
const arrivalTime = startTime.add(movementDuration.multiply(distance));

const parcels = Array.from(node.utility.parcels.keys());
res[`child_${idx}`] = {
intention: node.state.executedIntenion,
visits: node.visits,
utility_parcels: parcels,
score: node.utility.getValueByInstant(arrivalTime) / node.visits,
...this._getTree(node.children, arrivalTime, node.state.executedIntenion.position),
};
}

return res;
}
}
Loading

0 comments on commit 1ce4710

Please sign in to comment.