diff --git a/src/index.html b/src/index.html index 1b8c633..e56f684 100644 --- a/src/index.html +++ b/src/index.html @@ -18,6 +18,10 @@ + + + + diff --git a/src/modules/configuration.js b/src/modules/configuration.js index 406dc4d..2163e2f 100644 --- a/src/modules/configuration.js +++ b/src/modules/configuration.js @@ -1,7 +1,7 @@ -export const UnitsInGroupCount = 6 -export const RowsOfGroupsCount = 6 -export const ColumnsOfGroupsCount = 3 -export const StorageCenterLocation = { x: 7, y: 8, w: UnitsInGroupCount, h: 1 } +const UnitsInGroupCount = 6 +const RowsOfGroupsCount = 6 +const ColumnsOfGroupsCount = 3 +const StorageCenterLocation = { x: 7, y: 8, w: UnitsInGroupCount, h: 1 } -export const UnitsCount = ((ColumnsOfGroupsCount * RowsOfGroupsCount) * UnitsInGroupCount +const UnitsCount = ((ColumnsOfGroupsCount * RowsOfGroupsCount) * UnitsInGroupCount - StorageCenterLocation.w * StorageCenterLocation.h) diff --git a/src/modules/knowledge.js b/src/modules/knowledge.js new file mode 100644 index 0000000..6123506 --- /dev/null +++ b/src/modules/knowledge.js @@ -0,0 +1,106 @@ +function flattenToUnique(array, view = x => x) { + return array.reduce((set, value) => { + const v = view(value) + console.log(v) + v && v.forEach(cat => set.add(cat)) + return set + }, new Set()) +} + +class SemanticNetwork { + /** + * @param {string} definition + */ + constructor(definition) { + /** + * @param {string} stmt + * @returns {[{ name: string; categories: string[]; locations: string[] }]} + */ + function parseStatement(stmt) { + const [name, ...keywords] = stmt.split(':').map(x => x.trim()) + const o = { name } + for (const keyword of keywords) { + const command = keyword[0] + const items = keyword.slice(1).split(',').map(x => x.trim()).filter(x => x.length > 0) + switch (command) { + case 'c': o.categories = items; break + case 'p': o.locations = items; break + } + } + return o + } + + this.data = definition.split('\n').filter(x => x.trim().length > 0) + .map(parseStatement) + } + + getAllNames() { + return this.data.map(({ name }) => name) + } + + getAllCategories() { + return flattenToUnique(this.data, ({ categories }) => categories) + } + + getAllLocations() { + return flattenToUnique(this.data, ({ locations }) => locations) + } + + findByName(nameToFind) { + return this.data.find(({ name }) => name === nameToFind) + } + + findAllByCategory(categoryToFind) { + return this.data.filter(({ categories }) => categories.indexOf(categoryToFind) >= 0) + } + + findAllByLocation(locationToFind) { + return this.data.filter(({ locations }) => locations.indexOf(locationToFind) >= 0) + } +} + +class AgentSemanticNetwork extends SemanticNetwork {} + +class Arrangement { + constructor() { + this.products = [...Array(UnitsCount)].map(() => ({ + product: '', + count: 0, + icon: '' + })) + } +} + +class Knowledge { + static semanticNetwork + static agentSemanticNetwork + static arrangement + + constructor(definition) { + Knowledge.fullSemanticNetwork = new SemanticNetwork(definition) + Knowledge.agentSemanticNetwork = new AgentSemanticNetwork(definition) + Knowledge.arrangement = new Arrangement() + } +} + +/* +keywords: + p - powiązane pomieszczenia / miejsca + c - category +*/ +new Knowledge(` + Piłka :c sport,rozrywka, piłka nożna + Kubek klubowy :c sport,rozrywka,piłka nożna :p kuchnia + Waga łazienkowa :c zdrowie,agd,elektronika :p łazienka + Czajnik elektryczny :c agd,elektronika,zdrowie :p kuchnia + Widelec :c agd,jedzenie,zastawa kuchenna :p kuchnia + Łyżka :c agd,jedzenie,zastawa kuchenna :p kuchnia + Nóż stołowy :c agd,jedzenie,zastawa kuchenna :p kuchnia + Nóż kuchenny :c agd,gotowanie,przyrząd kuchenny :p kuchnia + Deska do krojenia :cagd,gotowanie,przyrząd kuchenny :p kuchnia + Gąbki kuchenne :c agd,gotowanie,sprzątanie :p kuchnia + Worki na śmieci :c agd,gotowanie,sprzątanie :p kuchnia + Miska :c agd,jedzenie,gotowanie,zastawa kuchenna :p kuchnia + Laptop :c elektronika,gaming :p biuro,szkoła + Telefon :c elektronika :p biuro,szkoła +`) diff --git a/src/modules/shop.js b/src/modules/shop.js index a761e5d..8b6fa9a 100644 --- a/src/modules/shop.js +++ b/src/modules/shop.js @@ -1,22 +1,19 @@ -import { fromTable } from './utilities' -import { RowsOfGroupsCount, StorageCenterLocation, UnitsCount } from './configuration' - /** * Zwraca koordynaty półek przy danym kafelku ścieżki * @param {number} gridX * @param {number} gridY * @returns {{x:number;y:number}[]} Koordynaty sąsiednich półek do kafelka */ -export function nearbyStorageUnitsCoords(gridX, gridY) { +function nearbyStorageUnitsCoords(gridX, gridY) { function outsideOfStorageCenter(v) { const { x, y, w, h } = StorageCenterLocation return v.x <= x || v.x >= x + w || v.y <= y || v.y >= y + h } - return (gridX % UnitsInGroupCount == 0 + return (gridX % UnitsInGroupCount == 0 ? (gridY % 3 == 1 - ? /* skrzyżowanie */ [[-1, -1], [-1, 1], [1, -1], [1,1]] - : /* korytarz góra-dół */ [[-1, 0], [1, 0]]) - : /* korytarz lewo-prawo */ [[0, -1], [0, 1]]) + ? /* skrzyżowanie */[[-1, -1], [-1, 1], [1, -1], [1, 1]] + : /* korytarz góra-dół */[[-1, 0], [1, 0]]) + : /* korytarz lewo-prawo */[[0, -1], [0, 1]]) .map(([xoff, yoff]) => ({ x: gridX + xoff, y: yGrid + yoff })) .filter(outsideOfStorageCenter) } @@ -27,25 +24,19 @@ export function nearbyStorageUnitsCoords(gridX, gridY) { * @param {number} gridY * @returns {{x:number;y:number}[]} Indeksy sąsiednich półek do kafelka */ -export function nearbyStorageUnitsIndexes(gridX, gridY) { +function nearbyStorageUnitsIndexes(gridX, gridY) { return nearbyStorageUnitsCoords(gridX, gridY) .map(({ x, y }) => x * UnitsCount + y * UnitsCount * RowsOfGroupsCount) } -class Shop { - constructor() { - // pełna lista wszystkich możliwych produktów dostępnych w sklepie, - // w formacie [{ name: '', category: '', ... }, { name: '', category: '', ...}, ...] - // kolejność przedmiotów jest niezmienna, przez co można przechowywać ich indeksy zamiast referencji - this.products = fromTable( - ['name', 'category', 'intrest', '', 'icon'], - /* 0 */ ['Lodówka', 'AGD', 0.4, '', ''] - ) +function ensureArray(maybeArray) { + return Array.isArray(maybeArray) ? maybeArray : [] +} - this.productPlacement = [...Array(UnitsCount)].map(_ => ({ - productId: null, - count: 0, - })) +class Shop { + constructor(semanticNetwork) { + this.semanticNetwork = semanticNetwork + this.products = [...Array(UnitsCount)].map(x => ({ name: '', count: 0 })) } /** @@ -57,7 +48,24 @@ class Shop { return nearbyStorageUnitsIndexes(gridX, gridY) .map(i => this.productPlacement[i]) } + + productsSimilarityScore(name1, name2) { + const [ o1, o2 ] = [name1, name2].map(name => this.semanticNetwork.findByName(name)) + if (!o1 || !o2) + throw new Error("names should be in semantic network!") + + function calcScore(p) { + if (!o1[p] || !o2[p]) + return 0 + + const set = new Set([...o1[p], ...o2[p]]) + const matching = o1[p].reduce((s, v) => s + (o2[p].indexOf(v) >= 0 ? 1 : 0), 0) + return matching / [...set].lengthsss + } + + const [ l, c ] = [calcScore('locations'), calcScore('categories')] + return l <= Number.EPSILON ? c * 0.7 : l * 0.65 + c * 0.35 // TODO: maybe find better formula? needs testing + } } -const shop = new Shop() -export default shop +const shop = new Shop(semanticNetwork) diff --git a/src/modules/utilities.js b/src/modules/utilities.js index 2203003..d3fa400 100644 --- a/src/modules/utilities.js +++ b/src/modules/utilities.js @@ -5,7 +5,7 @@ * @param {...any[]} rows * @returns */ -export function fromTable(header, ...rows) { +function fromTable(header, ...rows) { function tupleToRecord(names, array) { return names.reduce((p, v, i) => ({ ...p, [v]: array[i] }), {}) } @@ -18,6 +18,6 @@ export function fromTable(header, ...rows) { * @param {number} lo Minimum return value * @param {number} hi Maximum return value * */ -export function clamp(v, lo, hi) { +function clamp(v, lo, hi) { return v <= lo ? lo : v >= hi ? hi : v }