Late night addition of semantic networks

This commit is contained in:
Robert Bendun 2021-03-29 03:29:05 +02:00
parent da5bcc30c7
commit 7c7a055a4a
5 changed files with 149 additions and 31 deletions

View File

@ -18,6 +18,10 @@
<script src="modules/agentController.js"></script>
<script src="modules/agent.js"></script>
<script src="modules/products.js"></script>
<script src="modules/knowledge.js"></script>
<script src="modules/configuration.js"></script>
<script src="modules/utilities.js"></script>
<script src="modules/shop.js"></script>
<!-- Main script file -->
<script src="main.js"></script>

View File

@ -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)

106
src/modules/knowledge.js Normal file
View File

@ -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
`)

View File

@ -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, '', '<path-to-icon>']
)
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)

View File

@ -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
}