Skip to content

Instantly share code, notes, and snippets.

@kenwebb
Created November 12, 2021 14:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kenwebb/6e1ac29031940ddf26e977bac593f137 to your computer and use it in GitHub Desktop.
Save kenwebb/6e1ac29031940ddf26e977bac593f137 to your computer and use it in GitHub Desktop.
Beach Comber
Beach Comber
For use with Island Game.
An Avatar that stays within the current stretch of Coast.
/**
Ken Webb
Learning Express.
October 25, 2021
see Xholon workbook: Express - web framework for node
3ede7991936fe5281657c494f8a92caf
(base) ken@ken-HP-ZBook-15-G2:~/nodespace/express01$ node app.js
Example app listening at http://localhost:3000
http://localhost:3000/add/1/99
result: 1 + 99 = 100
see also: ~/gwtspace/Xholon/Xholon/module/moduleAvatar2Server.xml
Performance
-----------
see: https://expressjs.com/en/advanced/best-practice-performance.html
To set NODE_ENV to “production”, enter this command in the Linux/Ubuntu terminal window, before running node:
export NODE_ENV=production
This is temporary, and is only active for that terminal window.
*/
const express = require('express')
const cors = require('cors') // KSW added this
const {ftest, random, random4, random8, nesw, bchcmr} = require('./functions') // Procedural Memory
const {stest, sinsert, squery} = require('./storage') // Declarative Memory
const app = express()
const port = 3000
// enable CORS for all origins
app.use(cors());
app.use(express.json()) // for parsing application/json
app.get('/', (req, res) => {
res.send('Hello World!')
})
// KSW test
app.get('/test1', (req, res) => {
res.send('Testing 1.')
})
// KSW test
app.get('/test2', (req, res) => {
res.send('Testing 2.')
})
app.get('/test2/:one', (req, res) => {
res.send('Testing 2: ' + req.params.one)
})
app.get('/add/:one/:two', (req, res) => {
res.send(`${req.params.one} + ${req.params.two} = ` + (0 + Number(req.params.one) + Number(req.params.two)))
})
/**
* Return a random direction for a Xholon agent to move to (0 1 2 3). Grid: (N S E W) or Tree: (parent first next prev).
*/
app.get('/moveto', (req, res) => {
res.send("" + random4())
})
/**
* Working Memory.
* @param {Request} req A Perception or similar Request.
* @param {Response} res A Motor command or similar Response.
*/
app.post('/nesw', (req, res) => {
sinsert(req.body)
res.send("" + nesw(req.body))
})
app.get("/squery", (req, res) => {
res.send(squery())
})
/**
* Beach Comber
* @param {Request} req A Perception or similar Request.
* @param {Response} res A Motor command or similar Response.
*/
app.post('/bchcmr', (req, res) => {
res.send("" + bchcmr(req.body))
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
console.log(ftest())
console.log(stest())
console.log("NODE_ENV: " + process.env.NODE_ENV)
})
/**
Ken Webb
October 31, 2021
functions.js
Functions for use with app.js
Decision Engine - Procedural Long-term Memory
*/
const ftest = () => "Hello out there from FTest!"
/**
* Various functions that generate random integers within specified ranges.
*/
const random = max => () => Math.floor(Math.random() * max)
const random4 = random(4)
const random8 = random(8)
/**
* Process a NESW JavaScript Object.
* @param {Object} obj ex: { me: 'LandCell', n: 'CoastCell', e: 'LandCell', s: 'LandCell', w: 'LandCell' }
* @return {Number} A direction to move to a random neighboring non-OceanCell gridCell.
*/
const nesw = obj => {
const neswArr = Object.entries(obj).reduce((prev, curr) => {
if ((curr[0] !== "me") && (curr[1] !== "OceanCell")) {
prev.push(curr[0])
}
return prev
}, [])
const moveto = neswArr[random(neswArr.length)()]
const movetoNum = moveto === "n" ? 0
: moveto === "e" ? 1
: moveto === "s" ? 2
: 3
//console.log(obj, neswArr, moveto, movetoNum)
return movetoNum
}
/**
* Process a Beach Comber JavaScript Object.
* @param {Object} obj ex: { me: 'CoastCell', n: 'CoastCell', e: 'LandCell', s: 'CoastCell', w: 'OceanCell' }
* @return {Number} A direction to move to a random neighboring CoastCell gridCell.
*/
const bchcmr = obj => {
const arr = Object.entries(obj).reduce((prev, curr) => {
if ((curr[0] !== "me") && (curr[1] === "CoastCell")) {
prev.push(curr[0])
}
return prev
}, [])
const moveto = arr[random(arr.length)()]
const movetoNum = moveto === "n" ? 0
: moveto === "e" ? 1
: moveto === "s" ? 2
: 3
//console.log(obj, arr, moveto, movetoNum)
return movetoNum
}
module.exports = {ftest, random, random4, random8, nesw, bchcmr};
<?xml version="1.0" encoding="UTF-8"?>
<!--
Xholon Avatar that moves within the Island Game world, always staying within the current stretch of coast.
Ken Webb
November 12, 2021
~/gwtspace/Xholon/Xholon/module/ava2srv/mBeachComber.xml
based on mAva2SrvPostNesw.xml
See Xholon workbook: Express - web framework for node
Copy and paste, or drag, this entire text into a running Xholon app.
-->
<XholonModule>
<XholonMap>
<Attribute_String roleName="ih"><![CDATA[
<_-.XholonClass>
<BeachComber superClass="Avatar"/>
<Talk2Server superClass="Script"/>
</_-.XholonClass>
]]></Attribute_String>
<Attribute_String roleName="cd"><![CDATA[
<xholonClassDetails>
<BeachComber><Color>purple</Color></BeachComber>
<Talk2Server><DefaultContent>
const REQ_REMOTE = true;
var me, ava, request, beh = {
postConfigure: function() {
me = this.cnode;
ava = me.parent().parent();
if (!ava["subtrees"]) {
ava.action("param subtrees true");
}
ava.action("param transcript false");
const url = ["http://127.0.0.1:3000/bchcmr", "http://192.168.0.39:3000/bchcmr"];
request = new Request(url[0]);
me.msg(101, "Sending me ...");
},
processReceivedMessage(msg) {
this.doRequest(this.prepData(ava.parent()));
},
prepData: function(pava) {
return {
me: pava.xhc().name(),
n: pava.port(0).xhc().name(),
e: pava.port(1).xhc().name(),
s: pava.port(2).xhc().name(),
w: pava.port(3).xhc().name()
}
},
doRequest: function(jso) {
if (REQ_REMOTE) {
this.doRequestRemote(jso);
}
else {
this.doRequestLocal(jso);
}
},
doRequestRemote: function(jso) {
fetch(request, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(jso)
})
.then((res) => res.text())
.then((responseText) => {
this.moveto(responseText);
me.msg(101, "Sending me againR ...");
return null;
});
},
doRequestLocal: function(obj) {
// this is the same code used in the Server functions.js
const arr = Object.entries(obj).reduce((prev, curr) => {
if ((curr[0] !== "me") &amp;&amp; (curr[1] === "CoastCell")) {
prev.push(curr[0])
}
return prev
}, [])
const random = max => () => Math.floor(Math.random() * max)
const moveto = arr[random(arr.length)()]
const movetoNum = moveto === "n" ? 0
: moveto === "e" ? 1
: moveto === "s" ? 2
: 3
this.moveto(movetoNum);
me.msg(101, "Sending me againL ...");
},
moveto: function(direction) {
ava.action("go " + direction); // go 0|1|2|3 OK
}
}
//# sourceURL=Talk2Serverbehavior.js
</DefaultContent></Talk2Server>
</xholonClassDetails>
]]></Attribute_String>
<Attribute_String roleName="csh"><![CDATA[
<_-.csh>
<!-- I can create additional instances of BeachComber, by dragging in just the following subtree -->
<BeachComber>
<BehaviorsST>
<Talk2Server/>
</BehaviorsST>
</BeachComber>
</_-.csh>
]]></Attribute_String>
</XholonMap>
</XholonModule>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment