chore: Priority quest pathfinding, orders window

This commit is contained in:
Marcin Czerniak 2021-06-20 19:53:17 +02:00
parent a760db4ef0
commit 4a1015fb5b
12 changed files with 1285 additions and 73 deletions

View File

@ -17,6 +17,7 @@
<!-- logic --> <!-- logic -->
<script src="configuration.js"></script> <script src="configuration.js"></script>
<script src="utilities.js"></script> <script src="utilities.js"></script>
<script src="logic/heap.js"></script>
<script src="logic/product.js"></script> <script src="logic/product.js"></script>
<script src="logic/knowledge.js"></script> <script src="logic/knowledge.js"></script>
<script src="logic/time.js"></script> <script src="logic/time.js"></script>
@ -26,6 +27,7 @@
<script src="view/floor.js"></script> <script src="view/floor.js"></script>
<script src="view/grid.js"></script> <script src="view/grid.js"></script>
<script src="view/ordersView.js"></script>
<script src="logic/pathfinding.js"></script> <script src="logic/pathfinding.js"></script>
<script src="view/timeController.js"></script> <script src="view/timeController.js"></script>
<script src="view/agentController.js"></script> <script src="view/agentController.js"></script>
@ -49,7 +51,9 @@
<main> <main>
<div class="header"> <div class="header">
<div class="header-line"></div> <div class="header-line">
<button onClick="OrdersView.instance.openWindow()">Make order</button>
</div>
<img src="img/logo.png" alt="logo"> <img src="img/logo.png" alt="logo">
<div class="header-line"> <div class="header-line">
<fieldset> <fieldset>
@ -301,5 +305,24 @@
</main> </main>
<div class="orders-window">
<div class="close" onClick="OrdersView.instance.closeWindow()"></div>
<div class="container">
<div class="line">
<div class="canvases"></div>
<div class="incrementals">
<div class="add" onClick="OrdersView.instance.addCanvas()">+</div>
<div class="substract" onClick="OrdersView.instance.removeCanvas()">-</div>
</div>
</div>
<div class="line gaps">
<div class="response">
<!-- PYTHON RESPONSE HERE -->
</div>
<button class="clear" onClick="OrdersView.instance.clear()">CLEAR</button>
<button class="order" onClick="OrdersView.instance.order()">ORDER</button>
</div>
</div>
</div>
</body> </body>
</html> </html>

View File

@ -8,6 +8,7 @@ class Agent extends AgentController {
Agent.instance = this; Agent.instance = this;
const cycle = async () => { const cycle = async () => {
try {
const jobRequest = Products.instance.getJobRequest(); const jobRequest = Products.instance.getJobRequest();
if (jobRequest) { if (jobRequest) {
await this.takeFromStore(jobRequest.product, jobRequest.amount); await this.takeFromStore(jobRequest.product, jobRequest.amount);
@ -17,7 +18,11 @@ class Agent extends AgentController {
} }
await waitFor(this.waitTime); await waitFor(this.waitTime);
cycle(); cycle();
} catch (e) {
console.log(e);
}
}; };
setTimeout(() => cycle(), 2000); setTimeout(() => cycle(), 2000);
@ -38,12 +43,16 @@ class Agent extends AgentController {
* @param {number} y Współrzędna y * @param {number} y Współrzędna y
*/ */
async deliver(x, y) { async deliver(x, y) {
try {
await this.moveTo(x, y); await this.moveTo(x, y);
await waitFor(this.waitTime); await this.waitFor(this.waitTime);
Products.instance.exchange(x, y, this.ownedProduct, 50); Products.instance.exchange(x, y, this.ownedProduct, 50);
this.ownedProductAmount = 0; this.ownedProductAmount = 0;
this.ownedProduct = null; this.ownedProduct = null;
} catch (e) {
throw e;
}
} }
/** /**
@ -51,10 +60,26 @@ class Agent extends AgentController {
* @param {*} product * @param {*} product
*/ */
async takeFromStore(product, amount) { async takeFromStore(product, amount) {
await this.moveTo(9, 8); try {
await waitFor(this.waitTime); await this.moveTo(8, 8);
await this.waitFor(this.waitTime);
this.ownedProductAmount = amount; this.ownedProductAmount = amount;
this.ownedProduct = product; this.ownedProduct = product;
} catch (e) {
throw e;
}
}
async deliverToOrders() {
try {
await this.moveTo(x, y);
await this.waitFor(this.waitTime);
this.ownedProductAmount = 0;
this.ownedProduct = null;
} catch (e) {
throw e;
}
} }
/** /**

869
src/logic/heap.js Normal file
View File

@ -0,0 +1,869 @@
/**
* Heap
* @type {Class}
*/
var Heap = /** @class */ (function () {
/**
* Heap instance constructor.
* @param {Function} compare Optional comparison function, defaults to Heap.minComparator<number>
*/
function Heap(compare) {
var _this = this;
if (compare === void 0) { compare = Heap.minComparator; }
this.compare = compare;
this.heapArray = [];
this._limit = 0;
/**
* Alias of add
*/
this.offer = this.add;
/**
* Alias of peek
*/
this.element = this.peek;
/**
* Alias of pop
*/
this.poll = this.pop;
/**
* Returns the inverse to the comparison function.
* @return {Function}
*/
this._invertedCompare = function (a, b) {
return -1 * _this.compare(a, b);
};
}
/*
Static methods
*/
/**
* Gets children indices for given index.
* @param {Number} idx Parent index
* @return {Array(Number)} Array of children indices
*/
Heap.getChildrenIndexOf = function (idx) {
return [idx * 2 + 1, idx * 2 + 2];
};
/**
* Gets parent index for given index.
* @param {Number} idx Children index
* @return {Number | undefined} Parent index, -1 if idx is 0
*/
Heap.getParentIndexOf = function (idx) {
if (idx <= 0) {
return -1;
}
var whichChildren = idx % 2 ? 1 : 2;
return Math.floor((idx - whichChildren) / 2);
};
/**
* Gets sibling index for given index.
* @param {Number} idx Children index
* @return {Number | undefined} Sibling index, -1 if idx is 0
*/
Heap.getSiblingIndexOf = function (idx) {
if (idx <= 0) {
return -1;
}
var whichChildren = idx % 2 ? 1 : -1;
return idx + whichChildren;
};
/**
* Min heap comparison function, default.
* @param {any} a First element
* @param {any} b Second element
* @return {Number} 0 if they're equal, positive if `a` goes up, negative if `b` goes up
*/
Heap.minComparator = function (a, b) {
if (a > b) {
return 1;
}
else if (a < b) {
return -1;
}
else {
return 0;
}
};
/**
* Max heap comparison function.
* @param {any} a First element
* @param {any} b Second element
* @return {Number} 0 if they're equal, positive if `a` goes up, negative if `b` goes up
*/
Heap.maxComparator = function (a, b) {
if (b > a) {
return 1;
}
else if (b < a) {
return -1;
}
else {
return 0;
}
};
/**
* Min number heap comparison function, default.
* @param {Number} a First element
* @param {Number} b Second element
* @return {Number} 0 if they're equal, positive if `a` goes up, negative if `b` goes up
*/
Heap.minComparatorNumber = function (a, b) {
return a - b;
};
/**
* Max number heap comparison function.
* @param {Number} a First element
* @param {Number} b Second element
* @return {Number} 0 if they're equal, positive if `a` goes up, negative if `b` goes up
*/
Heap.maxComparatorNumber = function (a, b) {
return b - a;
};
/**
* Default equality function.
* @param {any} a First element
* @param {any} b Second element
* @return {Boolean} True if equal, false otherwise
*/
Heap.defaultIsEqual = function (a, b) {
return a === b;
};
/**
* Prints a heap.
* @param {Heap} heap Heap to be printed
* @returns {String}
*/
Heap.print = function (heap) {
function deep(i) {
var pi = Heap.getParentIndexOf(i);
return Math.floor(Math.log2(pi + 1));
}
function repeat(str, times) {
var out = '';
for (; times > 0; --times) {
out += str;
}
return out;
}
var node = 0;
var lines = [];
var maxLines = deep(heap.length - 1) + 2;
var maxLength = 0;
while (node < heap.length) {
var i = deep(node) + 1;
if (node === 0) {
i = 0;
}
// Text representation
var nodeText = String(heap.get(node));
if (nodeText.length > maxLength) {
maxLength = nodeText.length;
}
// Add to line
lines[i] = lines[i] || [];
lines[i].push(nodeText);
node += 1;
}
return lines
.map(function (line, i) {
var times = Math.pow(2, maxLines - i) - 1;
return (repeat(' ', Math.floor(times / 2) * maxLength) +
line
.map(function (el) {
// centered
var half = (maxLength - el.length) / 2;
return repeat(' ', Math.ceil(half)) + el + repeat(' ', Math.floor(half));
})
.join(repeat(' ', times * maxLength)));
})
.join('\n');
};
/*
Python style
*/
/**
* Converts an array into an array-heap, in place
* @param {Array} arr Array to be modified
* @param {Function} compare Optional compare function
* @return {Heap} For convenience, it returns a Heap instance
*/
Heap.heapify = function (arr, compare) {
var heap = new Heap(compare);
heap.heapArray = arr;
heap.init();
return heap;
};
/**
* Extract the peek of an array-heap
* @param {Array} heapArr Array to be modified, should be a heap
* @param {Function} compare Optional compare function
* @return {any} Returns the extracted peek
*/
Heap.heappop = function (heapArr, compare) {
var heap = new Heap(compare);
heap.heapArray = heapArr;
return heap.pop();
};
/**
* Pushes a item into an array-heap
* @param {Array} heapArr Array to be modified, should be a heap
* @param {any} item Item to push
* @param {Function} compare Optional compare function
*/
Heap.heappush = function (heapArr, item, compare) {
var heap = new Heap(compare);
heap.heapArray = heapArr;
heap.push(item);
};
/**
* Push followed by pop, faster
* @param {Array} heapArr Array to be modified, should be a heap
* @param {any} item Item to push
* @param {Function} compare Optional compare function
* @return {any} Returns the extracted peek
*/
Heap.heappushpop = function (heapArr, item, compare) {
var heap = new Heap(compare);
heap.heapArray = heapArr;
return heap.pushpop(item);
};
/**
* Replace peek with item
* @param {Array} heapArr Array to be modified, should be a heap
* @param {any} item Item as replacement
* @param {Function} compare Optional compare function
* @return {any} Returns the extracted peek
*/
Heap.heapreplace = function (heapArr, item, compare) {
var heap = new Heap(compare);
heap.heapArray = heapArr;
return heap.replace(item);
};
/**
* Return the `n` most valuable elements of a heap-like Array
* @param {Array} heapArr Array, should be an array-heap
* @param {number} n Max number of elements
* @param {Function} compare Optional compare function
* @return {any} Elements
*/
Heap.heaptop = function (heapArr, n, compare) {
if (n === void 0) { n = 1; }
var heap = new Heap(compare);
heap.heapArray = heapArr;
return heap.top(n);
};
/**
* Return the `n` least valuable elements of a heap-like Array
* @param {Array} heapArr Array, should be an array-heap
* @param {number} n Max number of elements
* @param {Function} compare Optional compare function
* @return {any} Elements
*/
Heap.heapbottom = function (heapArr, n, compare) {
if (n === void 0) { n = 1; }
var heap = new Heap(compare);
heap.heapArray = heapArr;
return heap.bottom(n);
};
/**
* Return the `n` most valuable elements of an iterable
* @param {number} n Max number of elements
* @param {Iterable} Iterable Iterable list of elements
* @param {Function} compare Optional compare function
* @return {any} Elements
*/
Heap.nlargest = function (n, iterable, compare) {
var heap = new Heap(compare);
heap.heapArray = __spreadArrays(iterable);
heap.init();
return heap.top(n);
};
/**
* Return the `n` least valuable elements of an iterable
* @param {number} n Max number of elements
* @param {Iterable} Iterable Iterable list of elements
* @param {Function} compare Optional compare function
* @return {any} Elements
*/
Heap.nsmallest = function (n, iterable, compare) {
var heap = new Heap(compare);
heap.heapArray = __spreadArrays(iterable);
heap.init();
return heap.bottom(n);
};
/*
Instance methods
*/
/**
* Adds an element to the heap. Aliases: `offer`.
* Same as: push(element)
* @param {any} element Element to be added
* @return {Boolean} true
*/
Heap.prototype.add = function (element) {
this._sortNodeUp(this.heapArray.push(element) - 1);
this._applyLimit();
return true;
};
/**
* Adds an array of elements to the heap.
* Similar as: push(element, element, ...).
* @param {Array} elements Elements to be added
* @return {Boolean} true
*/
Heap.prototype.addAll = function (elements) {
var _a;
var i = this.length;
(_a = this.heapArray).push.apply(_a, elements);
for (var l = this.length; i < l; ++i) {
this._sortNodeUp(i);
}
this._applyLimit();
return true;
};
/**
* Return the bottom (lowest value) N elements of the heap.
*
* @param {Number} n Number of elements.
* @return {Array} Array of length <= N.
*/
Heap.prototype.bottom = function (n) {
if (n === void 0) { n = 1; }
if (this.heapArray.length === 0 || n <= 0) {
// Nothing to do
return [];
}
else if (this.heapArray.length === 1) {
// Just the peek
return [this.heapArray[0]];
}
else if (n >= this.heapArray.length) {
// The whole heap
return __spreadArrays(this.heapArray);
}
else {
// Some elements
var result = this._bottomN_push(~~n);
return result;
}
};
/**
* Check if the heap is sorted, useful for testing purposes.
* @return {Undefined | Element} Returns an element if something wrong is found, otherwise it's undefined
*/
Heap.prototype.check = function () {
var _this = this;
return this.heapArray.find(function (el, j) { return !!_this.getChildrenOf(j).find(function (ch) { return _this.compare(el, ch) > 0; }); });
};
/**
* Remove all of the elements from this heap.
*/
Heap.prototype.clear = function () {
this.heapArray = [];
};
/**
* Clone this heap
* @return {Heap}
*/
Heap.prototype.clone = function () {
var cloned = new Heap(this.comparator());
cloned.heapArray = this.toArray();
cloned._limit = this._limit;
return cloned;
};
/**
* Returns the comparison function.
* @return {Function}
*/
Heap.prototype.comparator = function () {
return this.compare;
};
/**
* Returns true if this queue contains the specified element.
* @param {any} o Element to be found
* @param {Function} fn Optional comparison function, receives (element, needle)
* @return {Boolean}
*/
Heap.prototype.contains = function (o, fn) {
if (fn === void 0) { fn = Heap.defaultIsEqual; }
return this.heapArray.findIndex(function (el) { return fn(el, o); }) >= 0;
};
/**
* Initialise a heap, sorting nodes
* @param {Array} array Optional initial state array
*/
Heap.prototype.init = function (array) {
if (array) {
this.heapArray = __spreadArrays(array);
}
for (var i = Math.floor(this.heapArray.length); i >= 0; --i) {
this._sortNodeDown(i);
}
this._applyLimit();
};
/**
* Test if the heap has no elements.
* @return {Boolean} True if no elements on the heap
*/
Heap.prototype.isEmpty = function () {
return this.length === 0;
};
/**
* Get the leafs of the tree (no children nodes)
*/
Heap.prototype.leafs = function () {
if (this.heapArray.length === 0) {
return [];
}
var pi = Heap.getParentIndexOf(this.heapArray.length - 1);
return this.heapArray.slice(pi + 1);
};
Object.defineProperty(Heap.prototype, "length", {
/**
* Length of the heap.
* @return {Number}
*/
get: function () {
return this.heapArray.length;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Heap.prototype, "limit", {
/**
* Get length limit of the heap.
* @return {Number}
*/
get: function () {
return this._limit;
},
/**
* Set length limit of the heap.
* @return {Number}
*/
set: function (_l) {
this._limit = ~~_l;
this._applyLimit();
},
enumerable: false,
configurable: true
});
/**
* Top node. Aliases: `element`.
* Same as: `top(1)[0]`
* @return {any} Top node
*/
Heap.prototype.peek = function () {
return this.heapArray[0];
};
/**
* Extract the top node (root). Aliases: `poll`.
* @return {any} Extracted top node, undefined if empty
*/
Heap.prototype.pop = function () {
var last = this.heapArray.pop();
if (this.length > 0 && last !== undefined) {
return this.replace(last);
}
return last;
};
/**
* Pushes element(s) to the heap.
* @param {...any} elements Elements to insert
* @return {Boolean} True if elements are present
*/
Heap.prototype.push = function () {
var elements = [];
for (var _i = 0; _i < arguments.length; _i++) {
elements[_i] = arguments[_i];
}
if (elements.length < 1) {
return false;
}
else if (elements.length === 1) {
return this.add(elements[0]);
}
else {
return this.addAll(elements);
}
};
/**
* Same as push & pop in sequence, but faster
* @param {any} element Element to insert
* @return {any} Extracted top node
*/
Heap.prototype.pushpop = function (element) {
var _a;
if (this.compare(this.heapArray[0], element) < 0) {
_a = [this.heapArray[0], element], element = _a[0], this.heapArray[0] = _a[1];
this._sortNodeDown(0);
}
return element;
};
/**
* Remove an element from the heap.
* @param {any} o Element to be found
* @param {Function} fn Optional function to compare
* @return {Boolean} True if the heap was modified
*/
Heap.prototype.remove = function (o, fn) {
if (fn === void 0) { fn = Heap.defaultIsEqual; }
if (this.length > 0) {
if (o === undefined) {
this.pop();
return true;
}
else {
var idx = this.heapArray.findIndex(function (el) { return fn(el, o); });
if (idx >= 0) {
if (idx === 0) {
this.pop();
}
else if (idx === this.length - 1) {
this.heapArray.pop();
}
else {
this.heapArray.splice(idx, 1, this.heapArray.pop());
this._sortNodeUp(idx);
this._sortNodeDown(idx);
}
return true;
}
}
}
return false;
};
/**
* Pop the current peek value, and add the new item.
* @param {any} element Element to replace peek
* @return {any} Old peek
*/
Heap.prototype.replace = function (element) {
var peek = this.heapArray[0];
this.heapArray[0] = element;
this._sortNodeDown(0);
return peek;
};
/**
* Size of the heap
* @return {Number}
*/
Heap.prototype.size = function () {
return this.length;
};
/**
* Return the top (highest value) N elements of the heap.
*
* @param {Number} n Number of elements.
* @return {Array} Array of length <= N.
*/
Heap.prototype.top = function (n) {
if (n === void 0) { n = 1; }
if (this.heapArray.length === 0 || n <= 0) {
// Nothing to do
return [];
}
else if (this.heapArray.length === 1 || n === 1) {
// Just the peek
return [this.heapArray[0]];
}
else if (n >= this.heapArray.length) {
// The whole peek
return __spreadArrays(this.heapArray);
}
else {
// Some elements
var result = this._topN_push(~~n);
return result;
}
};
/**
* Clone the heap's internal array
* @return {Array}
*/
Heap.prototype.toArray = function () {
return __spreadArrays(this.heapArray);
};
/**
* String output, call to Array.prototype.toString()
* @return {String}
*/
Heap.prototype.toString = function () {
return this.heapArray.toString();
};
/**
* Get the element at the given index.
* @param {Number} i Index to get
* @return {any} Element at that index
*/
Heap.prototype.get = function (i) {
return this.heapArray[i];
};
/**
* Get the elements of these node's children
* @param {Number} idx Node index
* @return {Array(any)} Children elements
*/
Heap.prototype.getChildrenOf = function (idx) {
var _this = this;
return Heap.getChildrenIndexOf(idx)
.map(function (i) { return _this.heapArray[i]; })
.filter(function (e) { return e !== undefined; });
};
/**
* Get the element of this node's parent
* @param {Number} idx Node index
* @return {any} Parent element
*/
Heap.prototype.getParentOf = function (idx) {
var pi = Heap.getParentIndexOf(idx);
return this.heapArray[pi];
};
/**
* Iterator interface
*/
Heap.prototype[Symbol.iterator] = function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.length) return [3 /*break*/, 2];
return [4 /*yield*/, this.pop()];
case 1:
_a.sent();
return [3 /*break*/, 0];
case 2: return [2 /*return*/];
}
});
};
/**
* Returns an iterator. To comply with Java interface.
*/
Heap.prototype.iterator = function () {
return this;
};
/**
* Limit heap size if needed
*/
Heap.prototype._applyLimit = function () {
if (this._limit && this._limit < this.heapArray.length) {
var rm = this.heapArray.length - this._limit;
// It's much faster than splice
while (rm) {
this.heapArray.pop();
--rm;
}
}
};
/**
* Return the bottom (lowest value) N elements of the heap, without corner cases, unsorted
*
* @param {Number} n Number of elements.
* @return {Array} Array of length <= N.
*/
Heap.prototype._bottomN_push = function (n) {
// Use an inverted heap
var bottomHeap = new Heap(this.compare);
bottomHeap.limit = n;
bottomHeap.heapArray = this.heapArray.slice(-n);
bottomHeap.init();
var startAt = this.heapArray.length - 1 - n;
var parentStartAt = Heap.getParentIndexOf(startAt);
var indices = [];
for (var i = startAt; i > parentStartAt; --i) {
indices.push(i);
}
var arr = this.heapArray;
while (indices.length) {
var i = indices.shift();
if (this.compare(arr[i], bottomHeap.peek()) > 0) {
bottomHeap.replace(arr[i]);
if (i % 2) {
indices.push(Heap.getParentIndexOf(i));
}
}
}
return bottomHeap.toArray();
};
/**
* Move a node to a new index, switching places
* @param {Number} j First node index
* @param {Number} k Another node index
*/
Heap.prototype._moveNode = function (j, k) {
var _a;
_a = [this.heapArray[k], this.heapArray[j]], this.heapArray[j] = _a[0], this.heapArray[k] = _a[1];
};
/**
* Move a node down the tree (to the leaves) to find a place where the heap is sorted.
* @param {Number} i Index of the node
*/
Heap.prototype._sortNodeDown = function (i) {
var _this = this;
var moveIt = i < this.heapArray.length - 1;
var self = this.heapArray[i];
var getPotentialParent = function (best, j) {
if (_this.heapArray.length > j && _this.compare(_this.heapArray[j], _this.heapArray[best]) < 0) {
best = j;
}
return best;
};
while (moveIt) {
var childrenIdx = Heap.getChildrenIndexOf(i);
var bestChildIndex = childrenIdx.reduce(getPotentialParent, childrenIdx[0]);
var bestChild = this.heapArray[bestChildIndex];
if (typeof bestChild !== 'undefined' && this.compare(self, bestChild) > 0) {
this._moveNode(i, bestChildIndex);
i = bestChildIndex;
}
else {
moveIt = false;
}
}
};
/**
* Move a node up the tree (to the root) to find a place where the heap is sorted.
* @param {Number} i Index of the node
*/
Heap.prototype._sortNodeUp = function (i) {
var moveIt = i > 0;
while (moveIt) {
var pi = Heap.getParentIndexOf(i);
if (pi >= 0 && this.compare(this.heapArray[pi], this.heapArray[i]) > 0) {
this._moveNode(i, pi);
i = pi;
}
else {
moveIt = false;
}
}
};
/**
* Return the top (highest value) N elements of the heap, without corner cases, unsorted
* Implementation: push.
*
* @param {Number} n Number of elements.
* @return {Array} Array of length <= N.
*/
Heap.prototype._topN_push = function (n) {
// Use an inverted heap
var topHeap = new Heap(this._invertedCompare);
topHeap.limit = n;
var indices = [0];
var arr = this.heapArray;
while (indices.length) {
var i = indices.shift();
if (i < arr.length) {
if (topHeap.length < n) {
topHeap.push(arr[i]);
indices.push.apply(indices, Heap.getChildrenIndexOf(i));
}
else if (this.compare(arr[i], topHeap.peek()) < 0) {
topHeap.replace(arr[i]);
indices.push.apply(indices, Heap.getChildrenIndexOf(i));
}
}
}
return topHeap.toArray();
};
/**
* Return the top (highest value) N elements of the heap, without corner cases, unsorted
* Implementation: init + push.
*
* @param {Number} n Number of elements.
* @return {Array} Array of length <= N.
*/
Heap.prototype._topN_fill = function (n) {
// Use an inverted heap
var heapArray = this.heapArray;
var topHeap = new Heap(this._invertedCompare);
topHeap.limit = n;
topHeap.heapArray = heapArray.slice(0, n);
topHeap.init();
var branch = Heap.getParentIndexOf(n - 1) + 1;
var indices = [];
for (var i = branch; i < n; ++i) {
indices.push.apply(indices, Heap.getChildrenIndexOf(i).filter(function (l) { return l < heapArray.length; }));
}
if ((n - 1) % 2) {
indices.push(n);
}
while (indices.length) {
var i = indices.shift();
if (i < heapArray.length) {
if (this.compare(heapArray[i], topHeap.peek()) < 0) {
topHeap.replace(heapArray[i]);
indices.push.apply(indices, Heap.getChildrenIndexOf(i));
}
}
}
return topHeap.toArray();
};
/**
* Return the top (highest value) N elements of the heap, without corner cases, unsorted
* Implementation: heap.
*
* @param {Number} n Number of elements.
* @return {Array} Array of length <= N.
*/
Heap.prototype._topN_heap = function (n) {
var topHeap = this.clone();
var result = [];
for (var i = 0; i < n; ++i) {
result.push(topHeap.pop());
}
return result;
};
/**
* Return index of the top element
* @param list
*/
Heap.prototype._topIdxOf = function (list) {
if (!list.length) {
return -1;
}
var idx = 0;
var top = list[idx];
for (var i = 1; i < list.length; ++i) {
var comp = this.compare(list[i], top);
if (comp < 0) {
idx = i;
top = list[i];
}
}
return idx;
};
/**
* Return the top element
* @param list
*/
Heap.prototype._topOf = function () {
var list = [];
for (var _i = 0; _i < arguments.length; _i++) {
list[_i] = arguments[_i];
}
var heap = new Heap(this.compare);
heap.init(list);
return heap.peek();
};
/**
* Swap identical elements compared by comparator function
*/
Heap.prototype.swap = function(el, cmp) {
if (!cmp) {
cmp = Heap.defaultIsEqual;
}
this.remove(el, cmp);
this.push(el);
};
/**
* Find element in heap
*/
Heap.prototype.getElement = function(o, cmp) {
if (!cmp) {
cmp = Heap.defaultIsEqual;
}
return this.heapArray.find(function (el) { return cmp(el, o); });
};
return Heap;
}());

View File

@ -10,13 +10,16 @@
const PATHFINDING_ACTION = Enum('ROTATE', 'MOVE') const PATHFINDING_ACTION = Enum('ROTATE', 'MOVE')
class Pathfinding { class Pathfinding {
static search(state, expectedState) { static search(state, expectedState) {
return Pathfinding.graphSearch({ state }, { state: expectedState }) return Pathfinding.graphSearch({ state }, { state: expectedState })
} }
static graphSearch(node, expectedNode, fringe = [], explored = []) { static priorityComparator(a, b) {
return a.totalCost - b.totalCost;
}
static graphSearch(node, expectedNode, fringe = new Heap(Pathfinding.priorityComparator), explored = []) {
fringe.push(node); fringe.push(node);
while(true) { while(true) {
@ -24,44 +27,47 @@ class Pathfinding {
return false; return false;
} }
const elem = fringe.shift(); const elem = fringe.pop();
if (Pathfinding.reached(elem.state, expectedNode.state)) { if (Pathfinding.isEqual(elem.state, expectedNode.state)) {
return Pathfinding.buildPath(node, elem); return Pathfinding.buildPath(node, elem);
} }
explored.push(elem); explored.push(elem);
for (const successor of this.successor(elem)) { for (const successor of this.successor(elem)) {
if (!Pathfinding.isNodeInArr(successor, explored) && !Pathfinding.isNodeInArr(successor, fringe)) { const isExplored = Pathfinding.isNodeInArr(successor, explored);
Pathfinding.insertWithPriority(successor, fringe); const elementFromFringe = fringe.getElement(successor, Pathfinding.isNodesEqual);
} else if (Pathfinding.isNodeInArr(successor, fringe)) { const isInFringe = !(elementFromFringe === undefined);
const index = Pathfinding.findIndexOf(successor, fringe);
if (successor.totalCost < fringe[index].totalCost) { if (!isExplored && !isInFringe) {
fringe.splice(index, 1, successor); fringe.push(successor);
} } else if (isInFringe && elementFromFringe.totalCost > successor.totalCost) {
fringe.swap(successor, Pathfinding.isNodesEqual);
} }
} }
// debugger;
} }
} }
static isNodeInArr(node, arr) { static isNodeInArr(node, arr) {
return !arr.every(n => !Pathfinding.reached(n.state, node.state)) return !arr.every(n => !Pathfinding.isEqual(n.state, node.state))
} }
static findIndexOf(node, arr) { // static findIndexOf(node, arr) {
return arr.findIndex(n => Pathfinding.reached(n.state, node.state)); // return arr.findIndex(n => Pathfinding.isEqual(n.state, node.state));
} // }
static insertWithPriority(node, arr) { // static insertWithPriority(node, arr) {
for (const [i, n] of arr.entries()) { // for (const [i, n] of arr.entries()) {
if (node.totalCost < n.totalCost) { // if (node.totalCost < n.totalCost) {
arr.splice(i, 0, node); // arr.splice(i, 0, node);
return; // return;
} // }
} // }
arr.push(node); // arr.push(node);
} // }
static successor(node) { static successor(node) {
const list = []; const list = [];
@ -105,7 +111,7 @@ class Pathfinding {
const result = []; const result = [];
let currentElement = expectedNode; let currentElement = expectedNode;
while(!Pathfinding.reached(currentElement.state, node.state)) { while(!Pathfinding.isEqual(currentElement.state, node.state)) {
result.unshift(currentElement); result.unshift(currentElement);
currentElement = currentElement.parent; currentElement = currentElement.parent;
} }
@ -113,12 +119,16 @@ class Pathfinding {
return result; return result;
} }
static getManhattanDistance(node, expectedNode) { // static getManhattanDistance(node, expectedNode) {
return Math.abs(node.state.position.x - expectedNode.state.position.x) + Math.abs(node.state.position.y - expectedNode.state.position.y); // return Math.abs(node.state.position.x - expectedNode.state.position.x) + Math.abs(node.state.position.y - expectedNode.state.position.y);
// }
static isEqual(state, expectedState) {
return state.rotation == expectedState.rotation && state.position.x == expectedState.position.x && state.position.y == expectedState.position.y;
} }
static reached(state, expectedState) { static isNodesEqual(node, expectedNode) {
return state.rotation == expectedState.rotation && state.position.x == expectedState.position.x && state.position.y == expectedState.position.y; return Pathfinding.isEqual(node.state, expectedNode.state);
} }
static getVectorFromRotation(rotation) { static getVectorFromRotation(rotation) {

View File

@ -23,7 +23,6 @@ class Product {
equals(other) { equals(other) {
return this.name === other.name; return this.name === other.name;
} }
<<<<<<< HEAD
static get REGISTRY() { static get REGISTRY() {
// in alphabetical order // in alphabetical order
@ -154,6 +153,4 @@ class Product {
static RANDOM_FROM_REGISTRY() { static RANDOM_FROM_REGISTRY() {
return Product.REGISTRY[Object.keys(Product.REGISTRY).filter(p => p !== 'empty')[Math.floor(Math.random() * (Object.keys(Product.REGISTRY).length - 1))]]; return Product.REGISTRY[Object.keys(Product.REGISTRY).filter(p => p !== 'empty')[Math.floor(Math.random() * (Object.keys(Product.REGISTRY).length - 1))]];
} }
=======
>>>>>>> 4bd5bbdc0a26639410a6634debaff60c8a484593
} }

View File

@ -1,10 +1,10 @@
const SHELF_TYPE = Object.freeze({ const SHELF_TYPE = Object.freeze({
FRIDGE_SHELF: 'FRIDGE_SHELF', FRIDGE_SHELF: 'lodowka',
LARGE_SHELF: 'LARGE_SHELF', LARGE_SHELF: 'gabarytowa',
BASKET_SHELF: 'BASKET_SHELF', BASKET_SHELF: 'kosze',
DISCOUNT_SHELF: 'DISCOUNT_SHELF', DISCOUNT_SHELF: 'przecena',
STANDARD_SHELF: 'STANDARD_SHELF', STANDARD_SHELF: 'standardowa',
FREESTANDING_SHELF: 'FREESTANDING_SHELF', FREESTANDING_SHELF: 'wolnostojaca',
}); });
class Shelf { class Shelf {

View File

@ -60,11 +60,13 @@ class Time {
async addDays(days) { async addDays(days) {
await Products.instance.next(days); await Products.instance.next(days);
Products.instance.update(); Products.instance.update();
const ul = document.querySelector('ul#history');
this.setDay(this.day += Number(days)); this.setDay(this.day += Number(days));
const ul = document.querySelector('ul#history');
const li = document.createElement('li'); const li = document.createElement('li');
li.innerHTML = `${this.day}: ${nice(Products.instance.max_similarity)}`; li.innerHTML = `${this.day}: ${nice(Products.instance.max_similarity)}`;
ul.appendChild(li); ul.appendChild(li);
Agent.instance.reset();
} }
} }

View File

@ -12,7 +12,8 @@ window.addEventListener('DOMContentLoaded', async () => {
const timeController = new TimeController(); const timeController = new TimeController();
const floor = new Floor(floorCanvas); const floor = new Floor(floorCanvas);
const grid = new Grid(gridCanvas); const grid = new Grid(gridCanvas);
const agent = new Agent(agentCanvas); new Agent(agentCanvas);
new OrdersView();
await Knowledge.load() await Knowledge.load()
const products = new Products(productsCanvas); const products = new Products(productsCanvas);

View File

@ -28,6 +28,7 @@ main {
width: 100%; width: 100%;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
z-index: 1;
} }
/* ######################################## */ /* ######################################## */
@ -48,6 +49,10 @@ main {
flex: 1; flex: 1;
} }
.header-line:first-of-type {
justify-content: flex-end;
}
.header-line fieldset { .header-line fieldset {
display: flex; display: flex;
gap: 5px; gap: 5px;
@ -84,6 +89,21 @@ main {
outline: 0; outline: 0;
} }
button {
background: transparent;
color: #3498db;
border: 1px solid #3498db;
border-radius: 5px;
padding: 10px 20px;
font-family: 'Montserrat', sans-serif;
font-size: 16px;
cursor: pointer;
}
button:hover {
background-color: #0A0A0A;
}
.logs-label { .logs-label {
margin: 0 20px 30px 10px; margin: 0 20px 30px 10px;
} }
@ -169,3 +189,91 @@ main {
#products i { #products i {
font-size: 20px; font-size: 20px;
} }
/* ######################################## */
/* # Orders window styling # */
/* ######################################## */
.orders-window {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.8);
z-index: 2;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
display: none;
}
.close::after {
position: absolute;
top: 0;
right: 0;
content: "\2716";
font-size: 30px;
color: white;
line-height: 100px;
text-align: center;
cursor: pointer;
display: flex;
width: 60px;
height: 60px;
justify-content: center;
align-items: center;
}
.orders-window canvas {
width: 100px;
height: 100px;
border: 1px solid black;
background-color: white;
}
.line {
display: flex;
}
.line.gaps > *:not(:first-child) {
margin-left: 10px;
}
.line .incrementals div {
width: 50px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
}
.line .incrementals {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: -0.25em;
}
.incrementals div {
border: 1px solid #3498db;
cursor: pointer;
border-radius: 5px;
}
.incrementals div:hover {
border: 1px solid #217bb8;
}
.response {
width: 100%;
flex-grow: 1;
display: flex;
align-items: center;
border-radius: 5px;
border: 1px solid #3498db;
background-color: black;
}

View File

@ -1,11 +1,13 @@
class AgentController { class AgentController {
constructor(canvas) { constructor(canvas) {
// Inicjalizacja // Inicjalizacja
this.canvas = canvas;
this.ctx = canvas.getContext('2d'); this.ctx = canvas.getContext('2d');
this.setCanvasSize(canvas); this.setCanvasSize(canvas);
this.ownedProduct = null; this.ownedProduct = null;
this.ownedProductAmount = 0; this.ownedProductAmount = 0;
this.isDestroyed = false;
// Akcje // Akcje
this.actions = { this.actions = {
@ -59,10 +61,18 @@ class AgentController {
if (nearestPoint) { if (nearestPoint) {
if (nearestPoint.action == PATHFINDING_ACTION.MOVE) { if (nearestPoint.action == PATHFINDING_ACTION.MOVE) {
try {
await this.moveInLine(nearestPoint.state.position, nearestPoint.actionCost); await this.moveInLine(nearestPoint.state.position, nearestPoint.actionCost);
} catch (e) {
return reject();
}
} }
if (nearestPoint.action == PATHFINDING_ACTION.ROTATE) { if (nearestPoint.action == PATHFINDING_ACTION.ROTATE) {
try {
await this.rotate({ z: nearestPoint.state.rotation }, nearestPoint.actionCost); await this.rotate({ z: nearestPoint.state.rotation }, nearestPoint.actionCost);
} catch (e) {
return reject();
}
} }
nearestPoint = this.currentPath[++this.currentPathIndex]; nearestPoint = this.currentPath[++this.currentPathIndex];
@ -96,6 +106,7 @@ class AgentController {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const cycle = () => { const cycle = () => {
if (this.currentAction == this.actions.STATIONARY) return resolve(); if (this.currentAction == this.actions.STATIONARY) return resolve();
if (this.isDestroyed) return reject();
let { speed } = this; let { speed } = this;
speed *= (actionCost - 1) * 4 + 1; speed *= (actionCost - 1) * 4 + 1;
@ -127,6 +138,11 @@ class AgentController {
}); });
} }
async waitFor() {
await waitFor(this.waitTime);
if (this.isDestroyed) throw new Error();
}
/** /**
* *
* @param {{ x: number, y: number }} position * @param {{ x: number, y: number }} position
@ -176,6 +192,8 @@ class AgentController {
})(); })();
const cycle = () => { const cycle = () => {
if (this.isDestroyed) return reject();
this.currentAction = this.actions.IN_ROTATION; this.currentAction = this.actions.IN_ROTATION;
const oldZRotation = this.rotation.z; const oldZRotation = this.rotation.z;
@ -297,11 +315,13 @@ class AgentController {
this.ctx.stroke(); this.ctx.stroke();
} }
destroy() {
this.isDestroyed = true;
this.update = () => {};
}
cancelAllActions() { cancelAllActions() {
this.ownedProduct = Product.REGISTRY.empty; Agent.instance.destroy();
this.ownedProductAmount = 0; Agent.instance = new Agent(this.canvas);
this.currentPath = [];
this.currentAction = this.actions.STATIONARY;
this.currentPathIndex = 0;
} }
} }

View File

@ -1,7 +1,8 @@
const GRID_FIELD_TYPE = Object.freeze({ const GRID_FIELD_TYPE = Object.freeze({
PATH: 'PATH', PATH: 'PATH',
SHELF: 'SHELF', SHELF: 'SHELF',
STORAGE: 'STORAGE' STORAGE: 'STORAGE',
ORDERS: 'ORDERS'
}); });
class Grid { class Grid {
@ -22,6 +23,7 @@ class Grid {
this.drawCostMap(); this.drawCostMap();
this.drawShelfs(); this.drawShelfs();
this.drawStorages(); this.drawStorages();
this.drawOrders();
Grid.instance = this; Grid.instance = this;
} }
@ -52,7 +54,7 @@ class Grid {
[...fact(1), GRID_FIELD_TYPE.PATH, ...fact(2), GRID_FIELD_TYPE.PATH, ...fact(2), GRID_FIELD_TYPE.PATH] [...fact(1), GRID_FIELD_TYPE.PATH, ...fact(2), GRID_FIELD_TYPE.PATH, ...fact(2), GRID_FIELD_TYPE.PATH]
); );
const lastLine = [...fact(6), GRID_FIELD_TYPE.PATH, ...fact(6, GRID_FIELD_TYPE.STORAGE), GRID_FIELD_TYPE.PATH, ...fact(6)]; const lastLine = [...fact(6), GRID_FIELD_TYPE.PATH, ...fact(3, GRID_FIELD_TYPE.STORAGE), ...fact(3, GRID_FIELD_TYPE.ORDERS), GRID_FIELD_TYPE.PATH, ...fact(6)];
for (let i = 0; i < 20; i++) { for (let i = 0; i < 20; i++) {
this.grid[i] = [...this.grid[i], lastLine[i]]; this.grid[i] = [...this.grid[i], lastLine[i]];
} }
@ -128,6 +130,35 @@ class Grid {
} }
} }
drawOrders () {
let isFirstOfType = true;
let isLastOfType = false;
let firstX;
for (let [x, line] of this.grid.entries()) {
for (let [y, type] of line.entries()) {
if (type === GRID_FIELD_TYPE.ORDERS) {
if (this.grid[x + 1] && this.grid[x + 1][y] !== GRID_FIELD_TYPE.ORDERS) {
isLastOfType = true;
}
if (isFirstOfType) {
isFirstOfType = false;
firstX = x;
this.orders(x * 100, y * 100, 100, 100, true, false, true, true);
} else if (isLastOfType) {
isLastOfType = false;
isFirstOfType = true;
this.orders(x * 100, y * 100, 100, 100, true, true, true, false);
this.ordersLabel(firstX * 100, y * 100, (x - firstX + 1) * 100, 100);
} else {
this.orders(x * 100, y * 100, 100, 100, true, false, true, false);
}
}
}
}
}
storeLabel(x, y, w, h) { storeLabel(x, y, w, h) {
let fontSize = 40; let fontSize = 40;
this.ctx.font = `${fontSize}px Montserrat`; this.ctx.font = `${fontSize}px Montserrat`;
@ -136,21 +167,46 @@ class Grid {
this.ctx.fillText("STORE", x + (w/2), y + (h/2) + 15); this.ctx.fillText("STORE", x + (w/2), y + (h/2) + 15);
} }
ordersLabel(x, y, w, h) {
let fontSize = 40;
this.ctx.font = `${fontSize}px Montserrat`;
this.ctx.textAlign = "center";
this.ctx.fillStyle = '#3498db';
this.ctx.fillText("ORDERS", x + (w/2), y + (h/2) + 15);
}
shelf (shelf, x, y, w, h) { shelf (shelf, x, y, w, h) {
this.ctx.strokeStyle = '#ffffff'; this.ctx.strokeStyle = '#ffffff';
this.ctx.lineWidth = 5; this.ctx.lineWidth = 5;
this.ctx.strokeRect(x, y, w, h); this.ctx.strokeRect(x, y, w, h);
this.drawFixIfOnEdge(x, y, w, h); this.drawFixIfOnEdge(x, y, w, h);
const descriptors = Object.getOwnPropertyDescriptors(SHELF_TYPE);
let imageSource = null;
for (const key of Object.keys(descriptors)) {
if (descriptors[key].value === shelf.type) {
imageSource = key;
}
}
if (imageSource && shelf.type !== SHELF_TYPE.STANDARD_SHELF) {
var img = new Image(); var img = new Image();
img.src = `img/shelves/${shelf.type}.svg`; img.src = `img/shelves/${imageSource}.svg`;
img.onload = () => { img.onload = () => {
this.ctx.drawImage(img, x, y, w, h); this.ctx.drawImage(img, x, y, w, h);
} }
} }
}
storage (x, y, w, h, hasTop, hasRight, hasBottom, hasLeft) { storage (x, y, w, h, hasTop, hasRight, hasBottom, hasLeft) {
this.ctx.strokeStyle = '#ffff00'; this.connectedSquare (x, y, w, h, hasTop, hasRight, hasBottom, hasLeft, '#ffff00');
}
orders (x, y, w, h, hasTop, hasRight, hasBottom, hasLeft) {
this.connectedSquare (x, y, w, h, hasTop, hasRight, hasBottom, hasLeft, '#3498db');
}
connectedSquare (x, y, w, h, hasTop, hasRight, hasBottom, hasLeft, color) {
this.ctx.strokeStyle = color;
this.ctx.lineWidth = 5; this.ctx.lineWidth = 5;
if (hasTop) { if (hasTop) {
this.ctx.beginPath(); this.ctx.beginPath();

101
src/view/ordersView.js Normal file
View File

@ -0,0 +1,101 @@
class OrdersView {
constructor() {
OrdersView.instance = this;
this.addMultipleCanvases(10);
}
order() {
const formData = new FormData();
Promise.all(
[...document.querySelectorAll('.orders-window .canvases canvas')].map((canv, index) => {
return new Promise((resolve, reject) => {
canv.toBlob(blob => {
formData.append(`file-${index}`, blob,`file-${index}.png`);
resolve();
});
});
})
).then(async () => {
const response = await fetch('/api/neural', {
method: 'POST',
body: formData
});
//const json = await response.json();
console.log(await response.text());
});
}
clear() {
[...document.querySelectorAll('.orders-window .canvases canvas')].forEach(canv => {
canv.getContext('2d').clearRect(0, 0, 100, 100);
});
}
openWindow() {
document.querySelector('.orders-window').style.display = 'flex';
}
closeWindow() {
document.querySelector('.orders-window').style.display = 'none';
}
addMultipleCanvases(amount) {
for (let i = 0; i < amount; i++) {
this.addCanvas();
}
}
addCanvas() {
const canv = document.createElement('canvas');
this.initializeDrawingOnCanvas(canv);
document.querySelector('.orders-window .canvases').append(canv);
}
removeCanvas() {
const c = document.querySelector('.orders-window .canvases');
c.removeChild(c.lastChild);
}
initializeDrawingOnCanvas(canvas) {
const ctx = canvas.getContext("2d");
let coord = { x: 0, y: 0 };
document.addEventListener("mousedown", start);
document.addEventListener("mouseup", stop);
window.addEventListener("resize", resize);
resize();
function resize() {
ctx.canvas.width = 100;
ctx.canvas.height = 100;
}
function reposition(event) {
coord.x = event.clientX - canvas.offsetLeft;
coord.y = event.clientY - canvas.offsetTop;
}
function start(event) {
document.addEventListener("mousemove", draw);
reposition(event);
}
function stop() {
document.removeEventListener("mousemove", draw);
}
function draw(event) {
ctx.beginPath();
ctx.lineWidth = 5;
ctx.lineCap = "round";
ctx.strokeStyle = "#000000";
ctx.moveTo(coord.x, coord.y);
reposition(event);
ctx.lineTo(coord.x, coord.y);
ctx.stroke();
}
}
}