Published
Edited
May 8, 2019
Insert cell
md`# Fallen Transponder Rendezvous Decoder
-------------
We're going to attempt to decode the Rendevouz string from the new quest line
The original string is:
\`${encrypted}\`
`
Insert cell
md`# Initial Visual Analysis
------------
First off, there's a repeating pattern that looks like: \`((3000)k18)\`. That is, two left-parenthesies followed by \`3000\` followed by a single right parenthesis, and then a group that consists of 1-letter and 1-or-2-digits. There is, however, one group that does not match this on the first line \`((3000b2))\`. I'm going to assume this is either a typo on Bungie's part, or a red herring. I'm going start by correcting this manually:
> \`${corrected}\`

Notice how, beginning with the first three characters \`1((\`, there is a pattern of numbers followed by two parenthesis, a set of characters, and then two closing parentheses followed by another stand alone numer. Let's get the matches based on those rules:

${formatLines(lines)}

The next thing we notice is that each line starts and ends with a \`((3000)<letter><number>)\` group. We need a name for this data structure, so I'm going to go on intuition and call them \`DIRECTIVES\` (in so much as I think these are either commands or coordinates, like on a grid with letter representing column and number representing row). More importantly, each line begins and ends with a directive that has the same \`row\` number, and subsequently per line, the \`row\` number diminishes by \`2\` (not including line 10). Further more, any directive given in the middle of a line has a \`row\` number that is less than the bounding \`row\` numbers given in the outter most directives of a line. Again, line \`10\` does not follow this pattern and appears to be anomalous. We will ignore this line for now.

Below is a list of directives for line \`1\`:

${tablify('Directives in text',directives)}

**MAX COLUMN:** ${maxBy('colNum',directives).colNum}


**MAX ROW:** ${maxBy('row',directives).row}


With this data, we can make a grid:

${tablifyGrid('The Grid',modifiedGrid)}

## Inserting Data Into The Grid

`
Insert cell
modifiedGrid = {
const g = {...grid}
directives.forEach(d => {
insertIntoGrid(d.column, d.row, d.insertions, g)
})
return g
}
Insert cell
insertIntoGrid = (startCol, startRow, insertions, g) => {
const str = insertions.reduce((agg, i) => `${agg}${i}`, '').replace('3000',' ')
const chars = str.split('')
const zCode = 'z'.charCodeAt(0)
const mapped = chars.map((c,i) => ({col: letterPlus(startCol, i), value:c }))
mapped.forEach(m => {
let colCode = m.col.charCodeAt(0)
let row = startRow
if(colCode > zCode) {
colCode -= 26
row += 1
}
const column = String.fromCharCode(colCode)

if(!!g[column]) {
g[column][row] = m.value
} else {
console.log('grid does not property:', column, colCode, m.value)
}
})
}
Insert cell
nextLetter = letter => letterPlus(letter, 1)
Insert cell
letterPlus = (letter, mod) => String.fromCharCode(letter.charCodeAt(0) + mod)
Insert cell
directives = pipe(findDirectives,addColNums)(lines)
Insert cell
addColNums = collection => collection.map(item => ({...item, colNum: item.column.charCodeAt(0) + 1 - minCharCode}))
Insert cell
minCharCode = 97
Insert cell
grid = {
const maxCol = 26
console.log('maxCol:', maxCol)
const maxRow = maxBy('row', directives).row + 1
console.log('maxRow:', maxRow)
const keys = [...Array(maxCol).keys()]
console.log('keys:', keys)
const cols = keys.map((x, i) => String.fromCharCode(i+minCharCode))
console.log('cols:', cols)
return cols.reduce((agg, col) => ({...agg, [col]: new Array(maxRow)}),{} )
}
Insert cell
maxBy = autocurry((key,collection) => collection.reduce((biggest, item) => max(biggest[key], item[key]) === item[key] ? item : biggest,{[key]: Number.MIN_VALUE}))
Insert cell
tablify = (title,collection) => {
const columns = [...keys(collection[0])]
const longest = reverse(sortBy('length', columns))[0].length
const barLength = longest > 5 ? longest : 5
const bar = [...Array(barLength).keys()].map(i => '-').join('')
const header = `| ${columns.join(' | ')} |`
const sep = `| ${columns.map(c => bar).join(' | ')} |`
const rows = collection.map((x, i) => `| ${i} | ${tail(columns).map(c => x[c] || '-').join(' | ')} |`).join('\n')

return `##### ${title}
${header}
${sep}
${rows}`
}
Insert cell
tablifyGrid = (title,g) => {
const gridKeys = keys(g)
const maxCol = reverse(gridKeys)[0]
const numCols = maxCol.charCodeAt(0) - minCharCode
const numRows = g[maxCol].length
const cols = [...Array(numCols+1).keys()].map((x, idx) => String.fromCharCode(idx + minCharCode))
const g2 = [...Array(numRows).keys()].map(rowNum => cols.reduce((agg, col) => ({
...agg,
[col]: g[col][rowNum]
}), {row:[]}))
return tablify(title, g2)
}
Insert cell
directivesrgx = /3000\)[a-z]\d{1,2}\)(.*?\(\(|$)/g
Insert cell
colrowrgx = /([a-z])(\d*)/
Insert cell
insertionsrgx =/\(([A-Z0-9]{4})\)/g
Insert cell
findDirectives = collection => collection.reduce((agg,line,idx) => {
console.group(`line[${idx}]`)
console.log(`line:`, line)
const matches = line.match(directivesrgx)
console.log('matches:', matches)
const elements = matches.map((m,i) => {
console.group('match spin')
const results = colrowrgx.exec(m)
console.log('results:', results)
if(results.length) {
const [input, column, row] = results
const insertions = []
let arr
while((arr = insertionsrgx.exec(m))!== null) {
console.log('insertions found:', arr)
insertions.push(arr[1])
}
console.groupEnd()
return {line: idx+1, column, row: parseInt(row), insertions}
}
console.groupEnd()
return null
})
.filter(x => !!x)
return [...agg, ...elements]
}, [])
Insert cell
Insert cell
md`## A Different Approach
Let's try seeing what happens when we replace all \`(3000)\` groups with spaces and remove parenthesis:
> ${pipeline(corrected)}

`
Insert cell
pipeline = pipe(
replaceSpaceTokens,
splitLines,

formatLines
)
Insert cell
formatLines = arr => arr.map(line => `>\`${line}\` \n`).join('\n')
Insert cell
splitLines = replace(/\d{1,2}\(/g,'\n\n(')
Insert cell
removeParens = replace(/(\(|\))/g,'')
Insert cell
replaceSpaceTokens = replace(/\(3000\)/g,' ')
Insert cell
nospaces = replaceSpaceTokens(corrected)
Insert cell
Insert cell
letterNumberStats = letterNumberGroups.reduce((agg, group) => {
if(!agg[group]) {
agg[group] = 0
}
agg[group]++
return agg
},{})


Insert cell
lines = tail(corrected.replace(/(\d{1,2}\(\()/g,'\n$&').split('\n'))
Insert cell
encrypted = '1((3000)o20)(JS01)((3000b2))(EA3Q)((3000)r20)2((3000)p18)(WJOS)(3000)(1J0E)(3000)(AT3W)(3000)(XW3G)((3000)k18)3((3000)a16)(JE0A)(3000)(TZOX)(3000)(WJOS)(1J3B)(3000)(AT3W)(3000)(XW3G)((3000)k16)4((3000)a14)(JE0A)(3000)(TZOX)(3000)(WJOS)((3000)a4)(JE3X)(3000)(TZ3U)(3000)(WJ3P)((3000)a14)5((3000)b12)(EAOT)(3000)(ZXOW)((3000)b6)(00Q7)((3000)a6)(JE3X)(3000)(TZ3U)((3000)o12)6((3000)b10)(SI0J)(3000)(EAOT)((3000)r4)(XWOJ)(S13G)((3000)w4)(ATOZ)(XW3G)((3000)k3)(IJ3B)(3000)(AT3W)((3000)p10)7((3000)o8)(JS01)(3000)(JE0A)((3000)14)(ZXOW)(JS3F)(JE3X)(3000)(TZ3U)(WJOS)(3000)(1J0E)(ATOZ)(XW3G)((3000)k3)(1J3B)(3000)(AT3W)((3000)p8)8((3000)o6)(JS01)(3000)(JE0A)((3000)14)(ZXOW)(005J)(005S)(0051)(005J)(EA3Q)(ZX3T)(3000)(JS01)(JE0A)(005T)(005Z)(005X)(005W)(JS3F)((3000)b4)(EA3Q)(3000)(ZX3T)((3000)b6)9((3000)k4)(1J0E)(3000)(ATOZ)((3000)p12)(WJOS)(0051)(005J)(EA3Q)((3000)02)(XW3G)(3000)(S13G)((3000)w4)10((3000)s5)(TZOX)((3000)o32)(JS3F)((3000)b5)'
Insert cell
corrected = '1((3000)o20)(JS01)((3000)b2)(EA3Q)((3000)r20)2((3000)p18)(WJOS)(3000)(1J0E)(3000)(AT3W)(3000)(XW3G)((3000)k18)3((3000)a16)(JE0A)(3000)(TZOX)(3000)(WJOS)(1J3B)(3000)(AT3W)(3000)(XW3G)((3000)k16)4((3000)a14)(JE0A)(3000)(TZOX)(3000)(WJOS)((3000)a4)(JE3X)(3000)(TZ3U)(3000)(WJ3P)((3000)a14)5((3000)b12)(EAOT)(3000)(ZXOW)((3000)b6)(00Q7)((3000)a6)(JE3X)(3000)(TZ3U)((3000)o12)6((3000)b10)(SI0J)(3000)(EAOT)((3000)r4)(XWOJ)(S13G)((3000)w4)(ATOZ)(XW3G)((3000)k3)(IJ3B)(3000)(AT3W)((3000)p10)7((3000)o8)(JS01)(3000)(JE0A)((3000)14)(ZXOW)(JS3F)(JE3X)(3000)(TZ3U)(WJOS)(3000)(1J0E)(ATOZ)(XW3G)((3000)k3)(1J3B)(3000)(AT3W)((3000)p8)8((3000)o6)(JS01)(3000)(JE0A)((3000)14)(ZXOW)(005J)(005S)(0051)(005J)(EA3Q)(ZX3T)(3000)(JS01)(JE0A)(005T)(005Z)(005X)(005W)(JS3F)((3000)b4)(EA3Q)(3000)(ZX3T)((3000)b6)9((3000)k4)(1J0E)(3000)(ATOZ)((3000)p12)(WJOS)(0051)(005J)(EA3Q)((3000)02)(XW3G)(3000)(S13G)((3000)w4)10((3000)s5)(TZOX)((3000)o32)(JS3F)((3000)b5)'
Insert cell
import {map, pipe, replace, split, keys, values, tail, reverse, sortBy, pickAll, autocurry} from '@ada-lovecraft/auto-fp'
Insert cell
max = Math.max
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more