first, create a spreadsheet to receive the form submissions.



once you have a spreadsheet set up, navigate to the Apps Script page.

paste this script:
function read_byte(bytes) {
let byte = bytes.splice(0, 1)[0];
return byte < 0 ? byte + 256 : byte;
}
function read_uint32(bytes) {
let a = read_byte(bytes);
let b = read_byte(bytes);
let c = read_byte(bytes);
let d = read_byte(bytes);
return a | (b << 8) | (c << 16) | (d << 24);
}
function read_int32(bytes) {
let a = read_byte(bytes);
let b = read_byte(bytes);
let c = read_byte(bytes);
let d = bytes.splice(0, 1)[0];
return a | (b << 8) | (c << 16) | (d << 24);
}
function read_string(bytes) {
let len = 0;
let shift = 0;
while (bytes.length > 0) {
let byte = read_byte(bytes);
len |= (byte & 0x7f) << shift;
shift += 7;
if (!(byte & 0x80))
break;
}
if (bytes.length < len)
len = bytes.length;
return String.fromCharCode(...bytes.splice(0, len));
}
function parseSolution(bytes, metrics) {
if (read_uint32(bytes) !== 7) {
console.log("invalid solution file");
return;
}
metrics.puzzle = read_string(bytes);
metrics.name = read_string(bytes);
if (read_uint32(bytes) === 0) {
console.log("solution file doesn't have metrics in it");
return;
}
let zero = read_uint32(bytes);
metrics.cycles = read_uint32(bytes);
let one = read_uint32(bytes);
metrics.cost = read_uint32(bytes);
let two = read_uint32(bytes);
metrics.area = read_uint32(bytes);
let three = read_uint32(bytes);
metrics.instructions = read_uint32(bytes);
if (zero !== 0 || one !== 1 || two !== 2 || three !== 3) {
console.log("invalid solution file");
return;
}
metrics["track hexes"] = 0;
let number_of_parts = read_uint32(bytes);
for (let i = 0; i < number_of_parts; ++i) {
let part_name = read_string(bytes);
metrics[part_name] = (metrics[part_name] || 0) + 1;
if (read_byte(bytes) != 1) {
console.log("invalid solution file");
return;
}
read_int32(bytes); // u position
read_int32(bytes); // v position
read_int32(bytes); // size
read_int32(bytes); // rotation
read_uint32(bytes); // input/output index
let number_of_instructions = read_uint32(bytes);
for (let j = 0; j < number_of_instructions; ++j) {
read_int32(bytes); // instruction offset
read_byte(bytes); // instruction type
}
if (part_name === "track") {
let number_of_track_hexes = read_uint32(bytes);
for (let j = 0; j < number_of_track_hexes; ++j) {
metrics["track hexes"]++;
read_int32(bytes); // u position
read_int32(bytes); // v position
}
}
read_uint32(bytes); // arm number
if (part_name === "pipe") {
read_uint32(bytes); // conduit id
let number_of_conduit_hexes = read_uint32(bytes);
for (let j = 0; j < number_of_conduit_hexes; ++j) {
read_int32(bytes); // u position
read_int32(bytes); // v position
}
}
}
return metrics;
}
function onSubmit(e) {
let ss = SpreadsheetApp.getActiveSpreadsheet();
for (var key in e.namedValues) {
let value = e.namedValues[key];
if (typeof value !== 'object' || value.length === 0)
continue;
let url = value[0].split('https://drive.google.com/open?id=');
if (url.length !== 2 || url[0] !== '')
continue;
let file = DriveApp.getFileById(url[1]);
let bytes = file.getBlob().getBytes();
let metrics = {};
for (var k in e.namedValues)
metrics[k] = e.namedValues[k][0];
parseSolution(bytes, metrics);
let sheet = ss.getSheetByName(key);
if (!sheet) {
sheet = ss.insertSheet(key);
sheet.setFrozenRows(1);
}
let row = [];
for (var m in metrics) {
let header = sheet.getRange(1, 1, 1, sheet.getMaxColumns()).getValues();
let col = header[0].indexOf(m);
if (col < 0) {
col = header[0].indexOf('');
sheet.getRange(1, col + 1).setValue(m);
}
row[col] = metrics[m];
}
sheet.appendRow(row);
}
}
into the text box. once it's set up, the script will automatically fill in the metrics in a separate sheet every time a form is submitted. be sure to save the file after you paste.

to make the code run on each submission, set up a trigger.


choose the onSubmit function…

and have it run on form submit:

you'll have to go through this scary alert. there may be a way for me to submit my code to google to avoid this, but it would take more than 24 hours. let me know and i can do it if you want, though.

once you see this page, it should be all set up. try submitting a solution and see if it works.

the sheet it creates should look something like this. the name of the sheet is based on the question name in the form.
