You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
4.6 KiB
JavaScript

let wasmInstance
const textEncoder = new TextEncoder()
const textDecoder = new TextDecoder()
const daySelector = document.querySelector('.selector select')
const output = document.querySelector('.output')
const inputTextArea = document.querySelector('textarea.input')
const outputTextArea1 = document.querySelector('textarea.output1')
const outputTextArea2 = document.querySelector('textarea.output2')
const codeView = document.querySelector('.code-view')
const runButton = document.querySelector('button')
async function getDayList() {
const days = []
for (let i = 1; i <= 25; i++) {
const response = await fetch(`/days/${i}/code.wat`, { method: 'head' })
if (response.status === 200) {
days.push(i)
}
}
console.clear()
for (const day of days) {
const option = document.createElement('option')
option.value = day
option.innerText = day
daySelector.appendChild(option)
}
document.querySelector('.loading').innerText = "Select a day"
document.querySelector('.loading').disabled = true
if (window.location.host.includes('tempest.local')) {
const lastDay = days[days.length - 1]
daySelector.value = lastDay
loadDay(lastDay).then(runDay)
}
}
async function loadDay(dayNum) {
output.className = ''
output.innerText = `loading day ${dayNum}... `
try {
const { WabtModule } = window;
const wabt = await WabtModule()
const url = `/days/${dayNum}/code.wat`
// Download wasm code
const wasmText = await fetch(url)
.then(res => res.text())
// initialize module
const module = wabt.parseWat(url, wasmText)
const { buffer: codeBuffer } = module.toBinary({
write_debug_names: true
})
const moduleImports = {
console: {
log1: (a) => console.log(a),
log2: (a, b) => console.log(a, b),
log3: (a, b, c) => console.log(a, b, c)
}
}
const { instance } = await WebAssembly.instantiate(codeBuffer, moduleImports)
wasmInstance = instance
outputTextArea1.value = ''
outputTextArea2.value = ''
codeView.innerHTML = wasmText
Prism.highlightElement(codeView)
} catch (err) {
output.className = 'error'
output.innerText = 'Error: ' + err.message
console.error(err)
return
}
try {
const inputText = await fetch(`/days/${dayNum}/input.txt`)
.then(res => res.text())
inputTextArea.value = inputText
} catch (err) {
inputTextArea.value = ''
console.error(err)
}
output.innerText = 'Loaded day ' + dayNum
}
function runPart(funcName, inputTextField, outputTextField) {
const { memory } = wasmInstance.exports;
const wasmBuffer = memory.buffer;
const inputBuffer = textEncoder.encode(inputTextField.value)
// Set length and file contents
const inputLength = inputBuffer.length
const inputLargeArray = new Uint32Array(wasmBuffer)
const inputSmallArray = new Uint8Array(wasmBuffer)
const fileInputNums = new Uint8Array(inputBuffer)
inputLargeArray[0] = inputLength
inputSmallArray.set(fileInputNums, 4)
// run the wasm
outputTextField.value = ""
const before = performance.now()
const outputLocation = wasmInstance.exports[funcName]()
const after = performance.now()
// Read the output
const outputBuffer = wasmBuffer.slice(outputLocation)
const outputLargeArray = new Uint32Array(outputBuffer.slice(0, 4))
const outputSmallArray = new Uint8Array(outputBuffer)
const outputLength = outputLargeArray[0]
const outputBytes = outputSmallArray.slice(4, 4 + outputLength)
const outputText = textDecoder.decode(outputBytes)
outputTextField.value = outputText
return after - before
}
function runDay() {
let part1run = null, part2run = null
try {
part1run = runPart('part1', inputTextArea, outputTextArea1).toFixed(2)
} catch (e) { console.error(e) }
try {
part2run = runPart('part2', inputTextArea, outputTextArea2).toFixed(2)
} catch (e) { console.error(e) }
output.className = 'success'
if (part1run && part2run) {
output.innerText = `success; part 1 runtime: ${part1run}ms, part 2 runtime: ${part2run}ms`
} else if (part1run) {
output.innerText = `partial success; part 1 runtime: ${part1run}ms, part 2: crashed`
} else if (part2run) {
output.innerText = `partial success; part 1: crashed, part 2 runtime: ${part2run}ms`
} else {
output.innerText = 'running wasm code failed'
output.className = 'error'
}
}
daySelector.value = ""
inputTextArea.value = ""
outputTextArea1.value = ""
outputTextArea2.value = ""
output.innerText = "Ready"
getDayList().then(() => {
daySelector.addEventListener('change', ev => {
loadDay(ev.target.value)
})
})
runButton.addEventListener('click', runDay)