Skip to content

Instantly share code, notes, and snippets.

@beardedtim
Created July 6, 2018 20:33
Show Gist options
  • Select an option

  • Save beardedtim/12b005c4a525acb31c1d80a619390dbf to your computer and use it in GitHub Desktop.

Select an option

Save beardedtim/12b005c4a525acb31c1d80a619390dbf to your computer and use it in GitHub Desktop.
A stupid worm
const B = require('bambi')
const EARTH_WIDTH = 5
const EARTH_HEIGHT = 5
const EMPTY = 0
const FOOD = 2
const SEEN = 4
const SELF = 8
const getValue = ({
x,
y
}, board) => board[y][x]
const setValue = ({
point,
value
}, board) => board[point.y][point.x] = value
const setFood = (location, board) =>
setValue({
point: location,
value: FOOD
}, board)
const setSelf = (location, board) =>
setValue({
point: location,
value: SELF
}, board)
const setSeen = (location, board) =>
setValue({
point: location,
value: SEEN
}, board)
/**
* Goal: Find the food
* Given: You only know what's in front of you and to your sides
*/
const createEarth = () => Array.from({
length: EARTH_HEIGHT
},
() => Array.from({
length: EARTH_WIDTH
}, () => EMPTY)
)
const createWormie = () => ({
location: {
x: 1,
y: 1
},
symbol: SELF
})
const currentVision = (location) => {
/**
* Wormie can see 1 block in any direction
*
* TODO: Add some linked-list-esque data features here
* so that when wormie looks at a location it can
* say "Look at the next few as well"
*/
const possible = []
// We can move left
if (location.x) {
possible.push({ ...location,
x: location.x - 1
})
}
// We can move up
if (location.y) {
possible.push({ ...location,
y: location.y - 1
})
}
// We can move right
if (location.x < EARTH_WIDTH - 1) {
possible.push({ ...location,
x: location.x + 1
})
}
// We can move down
if (location.y < EARTH_HEIGHT - 1) {
possible.push({ ...location,
y: location.y + 1
})
}
// We morph each into its own value
return possible.map(loc => ({
location: loc,
value: getValue(loc, earth)
}))
}
const getNextLocation = locations => {
const empties = []
const seen = []
for (let i = 0; i < locations.length; i++) {
const loc = locations[i]
// If it's a FOOD item
if (loc.value === FOOD) {
// Choose it right away!
return loc
}
// else, if it's empty
if (loc.value === EMPTY) {
// Let's save it for later
empties.push(loc)
}
if(loc.value === SEEN) {
seen.push(loc)
}
}
if (empties.length) {
// Select a random index between 0 and its max
const index = B.random(0, empties.length - 1)
// return the random location
return empties[index]
}
if(seen.length) {
const index = B.random(0, seen.length - 1)
return seen[index]
}
throw new Error(`END OF GAME. CANNOT MOVE`)
}
// A way to draw a board row by row
const drawBoard = (board = []) => {
for (let y = 0; y < board.length; y++) {
const row = []
for (let x = 0; x < board[y].length; x++) {
row[x] = board[y][x]
}
console.log(row)
}
}
let earth, Wormie
const isFood = ({
value
}) => value === FOOD
const updateEarth = newLocation => {
// Move Wormie
setSelf(newLocation, earth)
// Remove old wormie
setSeen(Wormie.location, earth)
}
const updateState = (visibleBlocks) => {
const nextLocation = getNextLocation(visibleBlocks)
// Update Earth State
updateEarth(nextLocation.location)
Wormie.location = nextLocation.location
return nextLocation
}
/**
* Amount of iterations to run simulation
*/
const EPOCS = 1000;
/**
* Max amount of tries any one simulation can have
*/
const MAX_TRIES = 100
/**
* STATEFUL
*/
/**
* The total amount of tries
* during the epoc
*/
let total = 0
/**
* Amount of tries spent on successful attempts
*/
let successfulTries = 0
/**
* Amount of tries spent on failed attempts
*/
let failureTries = 0
/**
* Amount of epocs that succeeded
*/
let succeeded = 0
/**
* Amount of epocs that failed
*/
let failed = 0
/**
* Flag to flip printings of epoc info
*/
const PRINT_HEADINGS = false
// For the total amount of EPOCS
for (let i = 0; i < EPOCS; i++) {
// we create a new world
earth = createEarth()
// and a new wormie
Wormie = createWormie()
const food = [{ x: 3, y: 3 }]
// and we set some values on the earth
food.forEach(f => setFood(f, earth))
setSelf(Wormie.location, earth)
if (PRINT_HEADINGS) {
console.log('***********BEGIN EPOC************')
console.log('***********BEGIN EPOC************')
}
// and each simulation needs state as well
// This is this specifics iterations count
let count = 0
// and the amount of food
let foodCount = food.length
while (true) {
count++
try {
if (count >= MAX_TRIES) {
throw new Error('MAX_TRIES')
}
// Before this iteration
if (PRINT_HEADINGS) {
console.log('***********PREMOVE************')
drawBoard(earth)
console.log('Wormie: ', Wormie)
console.log('***********PREMOVE************')
}
// The next location based on some heuristic
const nextLocation = updateState(currentVision(Wormie.location))
// If this next location is food
if (isFood(nextLocation)) {
foodCount--
}
// After updating state
if (PRINT_HEADINGS) {
console.log('***********POSTMOVE************')
drawBoard(earth)
console.log('Wormie: ', Wormie)
console.log('***********POSTMOVE************')
}
// Check for completed state
if (!foodCount) {
succeeded++
successfulTries += count
throw new Error('You are done! You caught all of the food!')
}
} catch (e) {
// console.warn(e)
// If we've reached max tries or we still have
// food when we get here
if(e.message.indexOf('MAX_TRIES') === 0 || foodCount) {
// treat it as a failure
failed++
failureTries += count
}
if (PRINT_HEADINGS) {
console.log('\n', e.message, '\n')
}
// Break out of the while loop
break;
}
}
// add this iteration's count to the total
total += count
if (PRINT_HEADINGS) {
console.log('***********END EPOC************')
console.log(`TOTAL MOVES: ${count}`)
console.log('***********END EPOC************')
}
}
console.log('***********END ALL************')
console.log(`TOTAL MOVES: ${total}`)
console.log(`TOTAL ITERATIONS: ${EPOCS}`)
console.log(`TOTAL SUCCESSES: ${succeeded}`)
console.log(`TOTAL FAILURES: ${failed}`)
console.log(`AVERAGE MOVES: ${total / EPOCS}`)
console.log(`TOTAL MOVES IN SUCCESSFUL TRIALS: ${successfulTries}`)
console.log(`AVG MOVES IN SUCCESSFUL TRIALS: ${successfulTries / succeeded}`)
console.log(`TOTAL MOVES IN FAILURE TRIALS: ${failureTries}`)
// console.log(`AVG MOVES IN FAILURE TRIALS: ${failureTries / failed}`)
console.log('***********END ALL************')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment