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