From 51bb4bf357d131dd77fcf9eaae121ead079ac006 Mon Sep 17 00:00:00 2001 From: Jan Dvorak Date: Thu, 4 Jul 2019 23:22:02 +0200 Subject: [PATCH] 2019-07-04 --- aoc2018.js | 555 ++++++++++++++++++++++++++++++++++++++ gzView.rb | 15 +- ident_subseq_dyn.rb | 106 ++++++-- ruby-snippets.rb | 70 +++++ tetris.rb | 78 ++++++ virtual_roadtrip_guide.rb | 90 +++++-- 6 files changed, 868 insertions(+), 46 deletions(-) create mode 100644 aoc2018.js create mode 100644 ruby-snippets.rb create mode 100644 tetris.rb diff --git a/aoc2018.js b/aoc2018.js new file mode 100644 index 0000000..63b616f --- /dev/null +++ b/aoc2018.js @@ -0,0 +1,555 @@ +// ==UserScript== +// @name Advent of code 2018 solutions +// @description Automatically fetches inputs and fills in solutions to AoC problems. +// @run-at document-load +// @grant none +// @include *://adventofcode.com/*/day/* +// @version 1.0 +// ==/UserScript== + +(async ()=>{ + const inputElem = document.querySelector(".puzzle-input"); + const inputLink = document.querySelector("a[href*='input']"); + const outputField = document.querySelector("input[name='answer']"); + let outputPara = outputField; + while(outputPara && !outputPara.parentElement.matches("main")) outputPara = outputPara.parentElement; + const day = location.pathname.split("/")[3]; + const part = document.querySelector(".day-success") ? "b" : "a" + + const solvers = window.solvers = { + "1a": input => input.trim().split("\n").map(l => +l).reduce((a, b) => a + b), + "1b": input => { + let curr = 0; const seen = [curr]; const changes = input.trim().split("\n").map(l => +l); + while(true) for(let c of changes){ + curr += c; if(seen.includes(curr)) return curr; seen.push(curr) + } + }, + "2a": input => { + const regex2 = new RegExp("abcdefghijklmnopqrstuvwxyz".split("").map(c=>`^[^${c}\n]*${c}[^${c}\n]*${c}[^${c}\n]*$`).join("|"), "mg"); + const regex3 = new RegExp("abcdefghijklmnopqrstuvwxyz".split("").map(c=>`^[^${c}\n]*${c}[^${c}\n]*${c}[^${c}\n]*${c}[^${c}\n]*$`).join("|"), "mg"); + console.log(input.match(regex2)); + console.log(input.match(regex3)); + return input.match(regex2).length * input.match(regex3).length; + }, + "2b": input => { + const ids = input.trim().split("\n"); + for(let i = 0; i < ids.length; i++){ + pairSearch: for(let j = 0; j < i; j++){ + let diffAt = -1; + for(let k = 0; k < ids[i].length; k++){ + if(ids[i][k] != ids[j][k]){ + if(diffAt != -1) continue pairSearch; + diffAt = k; + } + } + if(diffAt != -1){ + console.log(i, j) + return ids[i].slice(0, diffAt) + ids[i].slice(diffAt + 1); + } + } + } + }, + "3ab": (input, part) => { + const seenOnce = new Uint32Array(32 * 1024); + const seenAgain = new Uint32Array(32 * 1024); + for(let l of input.trim().split("\n")){ + const mr = l.match(/#(\d+) @ (\d+),(\d+): (\d+)x(\d+)/).map(l => +l); + byteFrom = mr[3] >> 5; + bitFrom = mr[3] & 31; + byteTo = (mr[3] + mr[5]) >> 5; + bitTo = (mr[3] + mr[5]) & 31; + + for(let row = mr[2]; row < mr[2] + mr[4]; row++){ + for(let byte = byteFrom; byte <= byteTo; byte++){ + mask = ~0; + if(byte == byteFrom) mask &= (-1 << bitFrom); + if(byte == byteTo) mask &= ~(-1 << bitTo); + const ix = row * 32 + byte; + seenAgain[ix] = seenAgain[ix] | (mask & seenOnce[ix]); + seenOnce[ix] = seenOnce[ix] | mask + } + } + } + + let count = 0; + for(let word of seenAgain){ + for(let bit = 0; bit < 32; bit++){ + count += (word >> bit) & 1; + } + } + if(part == "a") return count; + + search: for(let l of input.trim().split("\n")){ + const mr = l.match(/#(\d+) @ (\d+),(\d+): (\d+)x(\d+)/).map(l => +l); + byteFrom = mr[3] >> 5; + bitFrom = mr[3] & 31; + byteTo = (mr[3] + mr[5]) >> 5; + bitTo = (mr[3] + mr[5]) & 31; + + for(let row = mr[2]; row < mr[2] + mr[4]; row++){ + for(let byte = byteFrom; byte <= byteTo; byte++){ + mask = ~0; + if(byte == byteFrom) mask &= (-1 << bitFrom); + if(byte == byteTo) mask &= ~(-1 << bitTo); + const ix = row * 32 + byte; + if(seenAgain[ix] & mask) continue search; + } + } + + return mr[1]; + } + }, + "4a": input => { + const lines = input.trim().split("\n"); + lines.sort(); + + const minsSleptByGuard = {}; + let currGuard = null; let lastFellAsleep = null; + for(l of lines){ + mr = l.match(/\[\d\d\d\d-\d\d-\d\d \d\d:(\d\d)\] (.*#(\d+).*|.*)/); + + if(mr[3]){ + if(lastFellAsleep !== null) throw "previous guard still asleep!"; + currGuard = mr[3]; + minsSleptByGuard[currGuard] = minsSleptByGuard[currGuard] || 0; + } else if(mr[2] == "falls asleep"){ + if(lastFellAsleep !== null) throw "guard already asleep!"; + lastFellAsleep = +mr[1]; + } else if(mr[2] == "wakes up"){ + if(lastFellAsleep === null) throw "guard wasn't asleep!"; + minsSleptByGuard[currGuard] += +mr[1] - lastFellAsleep; + lastFellAsleep = null; + } + } + if(lastFellAsleep !== null) throw "last guard still asleep!"; + + guardChosen = Object.keys(minsSleptByGuard).sort((a, b) => minsSleptByGuard[b] - minsSleptByGuard[a])[0]; + console.log(guardChosen); + + let timesSleptPerMinute = []; + for(l of lines){ + mr = l.match(/\[\d\d\d\d-\d\d-\d\d \d\d:(\d\d)\] (.*#(\d+).*|.*)/); + + if(mr[3]){ + currGuard = mr[3]; + } else if(mr[2] == "falls asleep"){ + lastFellAsleep = +mr[1]; + } else if(mr[2] == "wakes up"){ + if(currGuard == guardChosen){ + for(let minute = lastFellAsleep; minute <= +mr[1]; minute++){ + timesSleptPerMinute[minute] = (timesSleptPerMinute[minute] || 0) + 1; + } + } + lastFellAsleep = null; + } + } + + const minuteChosen = Object.keys(timesSleptPerMinute).sort((a, b) => timesSleptPerMinute[b] - timesSleptPerMinute[a])[0]; + return Number (guardChosen) * +minuteChosen; + }, + "4b": input => { + const lines = input.trim().split("\n"); + lines.sort(); + + let lastFellAsleep = null; + const timesPerGuardAndMinute = {}; + for(l of lines){ + mr = l.match(/\[\d\d\d\d-\d\d-\d\d \d\d:(\d\d)\] (.*#(\d+).*|.*)/); + + + if(mr[3]){ + if(lastFellAsleep !== null) throw "previous guard still asleep!"; + currGuard = mr[3]; + } else if(mr[2] == "falls asleep"){ + if(lastFellAsleep !== null) throw "guard already asleep!"; + lastFellAsleep = +mr[1]; + } else if(mr[2] == "wakes up"){ + if(lastFellAsleep === null) throw "guard wasn't asleep!"; + for(let minute = lastFellAsleep; minute < +mr[1]; minute++){ + timesPerGuardAndMinute[`${currGuard},${minute}`] = (timesPerGuardAndMinute[`${currGuard},${minute}`] || 0) + 1; + } + lastFellAsleep = null; + } + } + if(lastFellAsleep !== null) throw "last guard still asleep!"; + + const chosenGuardAndMinute = Object.keys(timesPerGuardAndMinute).sort((a, b) => timesPerGuardAndMinute[b] - timesPerGuardAndMinute[a])[0]; + console.log(chosenGuardAndMinute); + return chosenGuardAndMinute.split(",").map(l => +l).reduce((a, b) => a*b); + }, + "5ab": (input, part) => { + const upcaseOffset = "A".charCodeAt(0) - "a".charCodeAt(0); + const solidus = "|".charCodeAt(0); + const partA = input => { + let iLen; + do{ + iLen = input.length; + for(let c = "a".charCodeAt(0); c <= "z".charCodeAt(0); c++){ + const re = new RegExp(String.fromCharCode(c, c + upcaseOffset, solidus, c+upcaseOffset, c), 'g'); + input = input.replace(re, ""); + } + }while(iLen > input.length); + return input.trim().length; + }; + + if(part == "a"){ + return partA(input); + } else { + let bestLength = 1/0; + for(let c = "a".charCodeAt(0); c <= "z".charCodeAt(0); c++){ + const re = new RegExp(String.fromCharCode(c, solidus, c+upcaseOffset), 'g'); + const iterLength = partA(input.replace(re, "")); + console.log(re, iterLength); + if(iterLength < bestLength) bestLength = iterLength; + } + return bestLength; + } + }, + "6a": input => { + const dests = input.trim().split(/\n/).map(l => l.split(", ").map(d => +d)); + const size = 1 + dests.flat().reduce((a, d) => a > +d ? a : +d, 0); + const histogram = []; + for(let x = 0; x < size; x++){ + for(let y = 0; y < size; y++){ + let closestElem = 0; + let closestDist = Math.abs(x - dests[0][0]) + Math.abs(y - dests[0][1]); + for (let destIx = 1; destIx < dests.length; destIx++){ + newDist = Math.abs(x - dests[destIx][0]) + Math.abs(y - dests[destIx][1]); + if(newDist < closestDist){ + closestDist = newDist; closestElem = destIx; + } else if(newDist == closestDist){ + closestElem = -1; + } + } + if(closestElem > -1){ + if(x == 0 || y == 0 || x == size - 1 || y == size - 1){ + histogram[closestElem] = 1/0; + } else { + histogram[closestElem] = (histogram[closestElem] || 0) + 1; + } + } + } + } + return histogram.reduce((a, b) => a > b || b == 1/0 ? a : b); + }, + "6b": input => { + const dests = input.trim().split(/\n/).map(l => l.split(", ").map(d => +d)); + const size = 1 + dests.flat().reduce((a, d) => a > +d ? a : +d, 0); + const margin = 0 | (10000 / dests.length); + let pointCount = 0; + for(let x = -margin; x < size + margin; x++){ + for(let y = -margin; y < size + margin; y++){ + const distSum = dests.reduce((a, d) => a + Math.abs(x - d[0]) + Math.abs(y - d[1]), 0); + if(distSum < 10000) pointCount++; + } + } + return pointCount; + }, + "7a": input => { + let todo = Array.from("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + let output = ""; + while(todo.length){ + next = todo.find(c => ! todo.some(pre => input.match(`Step ${pre} must be finished before step ${c} can begin`))); + output += next; + todo = todo.filter(t => t != next); + } + return output; + }, + "7b": input => { + let todo = Array.from("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + let undone = Array.from("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + let jobs = []; + let t = 0; + while(undone.length){ + for(let jobId of [0, 1, 2, 3, 4]){ + if(jobs[jobId]){ + jobs[jobId].time--; + if(jobs[jobId].time == 0){ + undone = undone.filter(t => t != jobs[jobId].task); + console.log(`@${t}#${jobId} task ${jobs[jobId].task} done`); + jobs[jobId] = null; + } + } + } + for(let jobId of [0, 1, 2, 3, 4]){ + if(!jobs[jobId]){ + next = todo.find(c => ! undone.some(pre => input.match(`Step ${pre} must be finished before step ${c} can begin`))); + if(next){ + jobs[jobId] = {time: 61 + next.charCodeAt(0) - "A".charCodeAt(0), task: next}; + console.log(`@${t}#${jobId} task ${next} ${jobs[jobId].time} left`); + todo = todo.filter(t => t != next); + } + } + } + if(! jobs.some(j => j)) return t; + t++; + } + }, + "8ab": (input, part) => { + const parseTree = input => { + const childCount = input[0]; + const metaCount = input[1]; + input = input.slice(2); + const children = []; + for(let i = 0; i < childCount; i++){ + let child; + [child, input] = parseTree(input); + children.push(child); + } + const metas = input.slice(0, metaCount); + return [{children, metas}, input.slice(metaCount)]; + } + + input = input.split(" ").map(w => +w); + [tree, input] = parseTree(input); + if(input.length) throw "input not empty after reading tree"; + + const sumMetas = node => node.children.reduce((a, d) => a + sumMetas(d), node.metas.reduce((a, d) => a + d, 0)); + const nodeValue = node => node.metas.map(m => + node.children.length == 0 ? m : + (m == 0 || m > node.children.length) ? 0 : + nodeValue(node.children[m - 1]) + ).reduce((a, d) => a + d, 0); + return {a: sumMetas, b: nodeValue}[part](tree); + }, + "9ab": (input, part) => { + const mr = input.match(/(\d+) players; last marble is worth (\d+) points\n/); + const scores = new Array(+mr[1]); + const marbles = [0]; + for(let t = 1; t < +mr[2] * (part == "a" ? 1 : 100); t++){ + if (t % 23){ + marbles.push(marbles.shift()); + marbles.push(t); + }else{ + marbles.unshift(...marbles.splice(-7)); + scores[t % scores.length] = (scores[t % scores.length] || 0) + t + marbles.pop(); + marbles.push(marbles.shift()); + } + } + return scores.reduce((a, d) => a > +d ? a : +d, 0); + }, + "10ab": (input, part) => { + const points = input.trim().split("\n").map( + l => l.match(/position=<\s*(-?\d+),\s*(-?\d+)> velocity=<\s*(-?\d+),\s*(-?\d+)>/) + .slice(1).map(n => +n) + ); + + let time = 0, timeEl; + changeTime = offset => { + points.forEach(p => {p[0] += p[2] * offset; p[1] += p[3] * offset}); + time += offset; + if(timeEl) timeEl.value = time; + } + + let dimX, dimY, prevSize; + do{ + prevSize = dimX * dimY; + changeTime(1); + dimX = points.reduce((a, d) => a > d[0] ? a : d[0], 0) - + points.reduce((a, d) => a < d[0] ? a : d[0], 0); + dimY = points.reduce((a, d) => a > d[1] ? a : d[1], 0) - + points.reduce((a, d) => a < d[1] ? a : d[1], 0); + }while(!prevSize || dimX * dimY < prevSize); + changeTime(-1); + + widgetP = document.createElement("p"); + widgetP.innerHTML = +` + +
+ + + + + +` + + const canvasContext = widgetP.querySelector("canvas").getContext("2d"); + //canvasContext.translate(dimX, dimY); + + const drawCanvas = () => { + canvasContext.clearRect(0, 0, 2*dimX+1, 2*dimY+1); + for(let p of points) canvasContext.fillRect(p[0]-0.5, p[1]-0.5, 1, 1); + } + + let timeout = null; + const buttonHandler = (repeat, offset) => () => { + clearTimeout(timeout); + changeTime(offset); + drawCanvas(); + if(repeat) timeout = setTimeout(buttonHandler(repeat, offset), 100); + } + widgetP.querySelector("#playRwd").onclick = buttonHandler(true, -1); + widgetP.querySelector("#subTime").onclick = buttonHandler(false, -1); + widgetP.querySelector("#addTime").onclick = buttonHandler(false, 1); + widgetP.querySelector("#playFwd").onclick = buttonHandler(true, 1); + + timeEl = widgetP.querySelector("#time"); + return widgetP; + }, + "11ab": (input, part) => { + const ary = []; + for(let y = 300; y; y--){ + ary[y] = []; + for(let x = 300; x; x--){ + ary[y][x] = (((x + 10) * y + +input) * (x + 10) / 100 | 0) % 10 - 5; + } + } + + let best = 0, bestAt = null; + for(let sizeM1 = (part == 'a' ? 2 : 0); sizeM1 < (part == 'a' ? 2 : 299); sizeM1++){ + console.log(`testing size ${sizeM1+1}`); + for(let y = 1; y <= 300 - sizeM1; y++){ + for(let x = 1; x <= 300 - sizeM1; x++){ + let sum = 0; + for(let dy = 0; dy <= sizeM1; dy++){ + for(let dx = 0; dx <= sizeM1; dx++){ + sum += ary[y+dy][x+dx]; + } + } + if(sum > best){ + best = sum; + bestAt = `${x},${y}${part == 'a' ? '' : `,${sizeM1 + 1}`}`; + console.log(bestAt); + } + } + } + } + return bestAt; + }, + "12ab": (input, part) => { + const stateScore = (state, left) => state.split("").map((e, i) => e == "#" ? i + left : 0).reduce((a, d) => a + d, 0); + + let state = input.match(/initial state: ([-.#]+)/)[1]; + let left = 0; + const transitions = {}; + for(let l of input.match(/[.#]{5} => [.#]/g)){ + const mr = l.split(" => "); + transitions[mr[0]] = mr[1]; + } + + for(let t = 0; t < (part == "a" ? 20 : 200); t++){ + state = `....${state}....`; + let next = ""; + for(let i = 0; i < state.length - 4; i++){ + next += transitions[state.slice(i, i+5)]; + } + + if(state.match(/#[.#]*#/)[0] == next.match(/#[.#]*#/)[0]){ + const scorePerTurn = (stateScore(state, left - 4 + next.match(/^\.*/)[0].length - 2) - + stateScore(state, left - 4)); + return stateScore(state, left - 4) + (50e9 - t) * scorePerTurn; + } + + left += next.match(/^\.*/)[0].length - 2; + state = next.match(/#[.#]*#/)[0]; + console.log(t, left, state, stateScore(state, left)); + } + return stateScore(state, left); + }, + "13ab": (input, part) => { + const world = input.split("\n").map(l => l.split("").map(c => + ({">": "->0", "v": "|v0", "<": "-<0", "^": "|^0"}[c] || c) + )); + + let carts = []; + for(let y = 0; y < world.length; y++){ + for(let x = 0; x < world[y].length; x++){ + if(world[y][x].length > 1) carts.push([y, x]); + } + } + + for(let t = 0;; t++){ + for(let c of carts){ + let [cy, cx] = c; + let dir, mem; + [world[cy][cx], dir, mem] = world[cy][cx].split(""); + if(!dir) continue; + switch(dir){ + case ">": cx++; break; + case "v": cy++; break; + case "<": cx--; break; + case "^": cy--; break; + default: debugger; + } + c[0] = cy; c[1] = cx; + switch(world[cy][cx]){ + case "-": break; + case "|": break; + case "/": dir = {">" : "^", "v": "<", "<": "v", "^": ">"}[dir]; break; + case "\\": dir = {">" : "v", "v": ">", "<": "^", "^": "<"}[dir]; break; + case "+": + dir = [{">" : "^", "v": ">", "<": "v", "^": "<"}, + {">" : ">", "v": "v", "<": "<", "^": "^"}, + {">" : "v", "v": "<", "<": "^", "^": ">"}][mem][dir]; + mem = ["1", "2", "0"][mem]; + break; + default: + if(world[cy][cx].length == 3){ + if(part == "a") return `${cx},${cy}`; + world[cy][cx] = world[cy][cx][0]; + continue; + }else{ + debugger; + throw "off rails!"; + } + } + world[cy][cx] += dir + mem; + } + carts = carts.filter(([cy, cx]) => world[cy][cx].length > 1); + if(carts.length == 1) return carts[0].reverse().join(","); + carts.sort(([y1, x1], [y2, x2]) => y1 != y2 ? y1 - y2 : x1 - x2); + } + }, + "14a": input => { + const board = new Uint8Array(+input + 11); + board[0] = 3; board[1] = 7; let blength = 2; + let i = 0, j = 1; + while((blength < +input + 10)){ + for(d of (board[i] + board[j] + "").split("")) board[blength++] = +d; + i += board[i] + 1; i %= blength; + j += board[j] + 1; j %= blength; + } + console.log(blength, board.length, board.slice(blength - 20, blength)); + return board.slice(+input, +input + 10).join(""); + }, + "14b": input => { + const board = [3, 7]; + let i = 0, j = 1; + while(!board.slice(-input.length - 1).join("").includes(input)){ + for(d of (board[i] + board[j] + "").split("")) board.push(+d); + i += board[i] + 1; i %= board.length; + j += board[j] + 1; j %= board.length; + } + console.log(board.length, board.slice(-20)); + return board.jois("").indexOf(+input); + } + } + + function asyncTimeout(ms){return new Promise((resolve, reject) => setTimeout(resolve, ms))} + + if(outputField) outputField.value = "fetching input"; + if(inputElem){ + input = inputElem.textContent; + }else if(localStorage.getItem("inputHref") == inputLink){ + input = localStorage.getItem("input"); + console.log("using cached input; size " + input.length); + }else{ + input = await (await fetch(inputLink.href)).text(); + localStorage.setItem("inputHref", inputLink); + localStorage.setItem("input", input); + } + if(outputField) outputField.value = "calculating"; + await asyncTimeout(0); + + if(solvers[day+part]) result = await solvers[day+part](input); + else result = await solvers[day+"ab"](input, part); + + if(typeof result == "string" || typeof result == "number"){ + outputField.value = result; + }else{ + for(r of document.querySelectorAll("#result")) r.parentElement.removeChild(r); + document.querySelector("main").insertBefore(result, outputPara); + result.id = "result" + } +})() diff --git a/gzView.rb b/gzView.rb index 356d89b..329f471 100644 --- a/gzView.rb +++ b/gzView.rb @@ -326,8 +326,8 @@ def show_parse_block bit_reader, out_buf, stats, quiet:, extrapolate: stats[:block_counts][code] += 1 if code < 256 out_buf << code.chr - print "@#{at} #{key} - #{code} - #{NEW_STR if stats[:block_counts][code] == 1}literal #{code.chr.inspect[1 .. -2]}".ljust_d(50) unless quiet - puts code.chr.inspect[1 .. -2] unless quiet + puts "@#{at} #{key} - #{code} - #{NEW_STR if stats[:block_counts][code] == 1}"\ + "literal #{code.chr.inspect[1 .. -2]}".ljust_d(50) + code.chr.inspect[1 .. -2] unless quiet stats[:lit_blocks] += 1 elsif code == 256 puts "@#{at} #{key} - #{code} - end of block" unless quiet @@ -360,9 +360,9 @@ def show_parse_block bit_reader, out_buf, stats, quiet:, extrapolate: puts ("@#{at} #{key} #{extra.join} #{okey} #{oextra.join} - repeat" + " #{NEW_STR if stats[:block_counts][code] == 1}#{length}" + " #{NEW_STR if stats[:offset_counts][ocode] == 1}#{offset}").ljust_d(45) + - "\e[31m#{out_buf[buf_before ... buf_start].inspect[1 .. -2]}\e[0m" + - "#{out_buf[buf_start ... buf_end].inspect[1 .. -2]}" + - "\e[31m#{out_buf[buf_end ... buf_after].inspect[1 .. -2]}\e[0m" + "\e[31m#{out_buf[buf_before ... buf_start].inspect[1 .. -2].gsub(" ", "•")}\e[0m" + + "#{out_buf[buf_start ... buf_end].inspect[1 .. -2].gsub(" ", "•")}" + + "\e[31m#{out_buf[buf_end ... buf_after].inspect[1 .. -2].gsub(" ", "•")}\e[0m" end stats[:rep_blocks] += 1 end @@ -380,8 +380,9 @@ def define_more(scan_to) loop do case key = $stdin.getch when " " - (IO.console.winsize[0] - 2).times do + (IO.console.winsize[0] - 2).times do |i| line = Fiber.yield + p line unless line.is_a? String orig_puts[line] break if line.include? NEW_STR end @@ -390,7 +391,7 @@ def define_more(scan_to) end end end - define_method(:puts){|*strs| strs.each{|str| fiber.resume str}} + define_method(:puts){|*args| args.each{|arg| [*arg].each{|str| fiber.resume str}}} end ################################################################################ diff --git a/ident_subseq_dyn.rb b/ident_subseq_dyn.rb index fefff36..a157af2 100644 --- a/ident_subseq_dyn.rb +++ b/ident_subseq_dyn.rb @@ -13,9 +13,57 @@ def inspect class IdentSubseqCalc def subseq_enum str Enumerator.new do |y| - ([(str.length-1 if @slow_mode), @min_chars].compact.max .. str.length).each do |sublen| - enum = str.chars.combination(sublen) - if @sort != [:ix] + str_capitals = str.count "A-Z" + str_minuscules = str.count "a-z" + str_symbols = str.length - str_capitals - str_minuscules + p [str, :all, str_symbols, str_minuscules, str_capitals] unless @recursive_mode + ([(str.length-1 if @slow_mode), [@min_chars, str.length].min].compact.max .. str.length).each do |sublen| + enum = Enumerator.new do |y2| + vals_uniq = Set.new + vals_total = 0 + str.chars.combination(sublen).each do |sub| + y2.yield(sub) unless @count_uniq && vals_uniq.include?(sub) + vals_uniq << sub if @count_uniq && @sort != [:caps_first, :ix] + vals_total += 1 + end + if !@recursive_mode && @sort != [:caps_first, :ix] + if @count_uniq + p [str, sublen, "#{vals_uniq.count}/#{vals_total}"] + else + p [str, sublen, "???/#{vals_total}"] + end + end + end + case @sort + when [:ix] then nil + when [:caps_first, :ix] + length_enum = enum + enum = Enumerator.new do |y2| + 0.upto([str_symbols, sublen].min) do |sub_symbols| + 0.upto([str_minuscules, sublen - sub_symbols].min) do |sub_minuscules| + sub_capitals = sublen - sub_minuscules - sub_symbols + if sub_capitals <= str_capitals + vals_uniq = Set.new + vals_total = 0 + length_enum.each do |sub| + if sub.join.count("A-Z") == sub_capitals && sub.join.count("a-z") == sub_minuscules + y2.yield(sub) unless @count_uniq && vals_uniq.include?(sub) + vals_uniq << sub if @count_uniq + vals_total += 1 + end + end + if !@recursive_mode + if @count_uniq + p [str, sub_symbols, sub_minuscules, sub_capitals, "#{vals_uniq.count}/#{vals_total}"] + else + p [str, sub_symbols, sub_minuscules, sub_capitals, "???/#{vals_total}"] + end + end + end + end + end + end + else enum = enum.sort_by.with_index do |elem, ix| [*@sort].map do |key| case key @@ -30,7 +78,7 @@ def subseq_enum str end case @tiebreak when :first - enum.each{|c|puts "#{highlight_diff(str, c.join, "")}\eK\eA" if rand < 0.001; y.yield c.join} + enum.each{|c|puts "#{highlight_diff(str, c.join, "")}\e[1A" if rand < 0.001; y.yield c.join} when :all y.yield enum.to_a.map(&:join)#.uniq.sort else @@ -41,20 +89,24 @@ def subseq_enum str end attr_reader :strs - def initialize(letter_only: false, tiebreak: :first, slow_mode: false, sort: nil, redundant_mode: false, min_chars: 0) + def initialize(letter_only: false, tiebreak: :first, slow_mode: false, sort: nil, + count_uniq: false, redundant_mode: false, min_chars: 0, recursive_mode: false) @strs = [] @enums = [] @tiebreak = tiebreak @letter_only = letter_only @slow_mode = slow_mode @sort = [*sort] | [:ix] + @count_uniq = count_uniq @redundant_mode = redundant_mode @min_chars = min_chars + @recursive_mode = recursive_mode + @result_cache = [] end def dup_empty IdentSubseqCalc.new(letter_only: @letter_only, tiebreak: @tiebreak, - slow_mode: @slow_mode, sort: @sort, redundant_mode: @redundant_mode, + slow_mode: @slow_mode, sort: @sort, count_uniq: @count_uniq, redundant_mode: @redundant_mode, min_chars: @min_chars) end @@ -75,8 +127,7 @@ def push_all strs self end - def regexp_for k - redundancy = @redundant_mode ? 1 : 0 + def regexp_for k, redundancy sub_regexps = k.chars.combination(k.size - redundancy).map do |ks| ks.map{|c| Regexp.escape c}.join(".*") end @@ -88,12 +139,34 @@ def result ix str = strs[ix] enum = @enums[ix] str = str.gsub(/[^a-zA-Z]/, "") if @letter_only - strRegex = regexp_for str + strRegex = regexp_for str, 0 + strRegex2 = regexp_for str, 1 conflicts = @strs.reject {|s2| s2 =~ strRegex} + conflicts2 = @strs.reject {|s2| s2 =~ strRegex2} + new_conflicts = @strs.drop(@result_cache.size) & conflicts + new_conflicts2 = @strs.drop(@result_cache.size) & conflicts2 - filter = lambda do |str| - strRegex = regexp_for str - conflicts.any? {|s2| s2 =~ strRegex} + filter = if @redundant_mode + conflicts -= conflicts2 + new_conflicts -= new_conflicts2 + lambda do |str| + strRegex = regexp_for str, 0 + strRegex2 = regexp_for str, 1 + if @result_cache[ix] == str + new_conflicts2.any? {|s2| s2 =~ strRegex2} || new_conflicts.any? {|s2| s2 =~ strRegex} + else + conflicts2.any? {|s2| s2 =~ strRegex2} || conflicts.any? {|s2| s2 =~ strRegex} + end + end + else + lambda do |str| + strRegex = regexp_for str, 0 + if @result_cache[ix] == str + new_conflicts.any? {|s2| s2 =~ strRegex} + else + conflicts.any? {|s2| s2 =~ strRegex} + end + end end enum.next unless enum.peek @@ -124,7 +197,7 @@ def lenthen_result ix end end - def results; (0...strs.count).map{|i| result i}; end + def results; @result_cache = (0...strs.count).map{|i| result i}; end end ################################################################################ @@ -176,7 +249,7 @@ def highlight_diff in_str, new_str, old_str when [true, true] then "33;1" # bright yellow end "\e[#{color}m#{c}\e[0m" - end.join + end.join.gsub(" ", "•") end ################################################################################ @@ -192,6 +265,7 @@ def highlight_diff in_str, new_str, old_str recursive_mode = ARGV.include?("-r") || animation_mode slow_mode = ARGV.include?("-s") show_time = ARGV.include?("-t") + count_uniq = ARGV.include?("--count-uniq") letter_only = ARGV.include?("-w") tiebreak = ARGV.include?("--all") ? :all : :first @@ -204,8 +278,8 @@ def highlight_diff in_str, new_str, old_str exit end - calc = IdentSubseqCalc.new(tiebreak: tiebreak, letter_only: letter_only, sort: sort, - slow_mode: slow_mode, redundant_mode: redundant_mode, min_chars: min_chars) + calc = IdentSubseqCalc.new(tiebreak: tiebreak, letter_only: letter_only, sort: sort, count_uniq: count_uniq, + slow_mode: slow_mode, redundant_mode: redundant_mode, min_chars: min_chars, recursive_mode: recursive_mode) calc = RecursiveSubseqCalc.new(calc, animation_mode: animation_mode, slow_mode: slow_mode) if recursive_mode results = [] diff --git a/ruby-snippets.rb b/ruby-snippets.rb new file mode 100644 index 0000000..a07d7db --- /dev/null +++ b/ruby-snippets.rb @@ -0,0 +1,70 @@ +def gc x; (255 * x**(1/2.2)).round; end + +def find_collatz(x, triple_even = true) + paths_to = {x => [""]} + search_queue = [x] + search_queue.each do |n| + return paths_to[n].map do |path| + path.split("*").map{|part| part.length.to_s(16)}.join.gsub(/00(?=(00)*[^0])/, ";") + end if n == 1 + [(n/2 if n.even?), (3*n+1 if n.odd? || triple_even)].compact.each do |nn| + npaths = paths_to[n].map{|npath| npath + (nn > n ? "*" : "/")} + if paths_to[nn] + paths_to[nn].concat npaths if (paths_to[nn][0].length == npaths[0].length) + else + paths_to[nn] = npaths + search_queue << nn + end + end + end + return nil +end + +def combinations_3(x, y, z, n) + 0.upto(n) do |n_x| + 0.upto(n-n_x) do |n_y| + n_z = n - n_x - n_y + c = 1 + [[x, n_x], [y, n_y], [z, n_z]].each do |(d, n_d)| + c *= d.downto(d - n_d + 1).reduce(1, :*) / 1.upto(n_d).reduce(1, :*) + end + print "#{c} " + end + puts + end +end + +def ext_euclid(m, n) + q = [nil, nil] + r = [m,n] + c_n = [1, 0] + c_m = [0, 1] + loop.with_index(2) do |_, i| + q[i] = r[i-2] / r[i-1] + [r, c_m, c_n].each{|col| col[i] = col[i-2] - col[i-1] * q[i]} + return [q, r, c_m, c_n].transpose if r[i] == 0 + end +end + +def generate_group(first, gens, &op) + gen_at = gens.map{0} + elems = [first] + loop do + (0...gens.count).find do |gen_ix| + new_elem = op.(elems[gen_at[gen_ix]], gens[gen_ix]) + unless elems.include?(new_elem) + elems << new_elem + gen_at[gen_ix] += 1 + end + end or return elems + end +end + +def digitwise_sum(b) + f = Proc.new{|x, y| (x + y) % b + (x < b && y < b ? 0 : f[x/b, y/b] * b)} + +x=[*0..99].shuffle; x.each_slice(20).map{|x| puts x.join " "} +puts x.map{|x|(x/10).to_s}.join; puts x.map{|x|(x%10).to_s}.join +x.map{|e|puts e;g=gets; x<< g+e.to_s if g[/./]} + +h="";loop{h+=rand(2).to_s;print h;h="" if gets[/./]} diff --git a/tetris.rb b/tetris.rb new file mode 100644 index 0000000..a57532e --- /dev/null +++ b/tetris.rb @@ -0,0 +1,78 @@ +def overlay_bitmap(bitmap, mask, over_char, offset) + (0 ... mask.size).each do |i| + (0 ... mask[i].size).each do |j| + unless mask[i][j].nil? + bitmap[i + offset[0]][j + offset[1]] = over_char || mask[i][j] + end + end + end +end + +TETROMINO_STR = < w_a) > 0 ? [d, w_d] : [a, w_a] + end.first + end end def dms_to_radians(str) @@ -45,47 +62,65 @@ def dms_to_radians(str) def geo_dist(from, to) return 40000 if [from.lat, from.lon, to.lat, to.lon].any?(&:nil?) EARTH_DIAMETER * Math.asin(Math.sqrt( - Math.cos(from.lon) * Math.cos(to.lon) * - Math.sin((from.lat - to.lat)/2) ** 2 + - Math.sin((from.lon - to.lon)/2) ** 2 + Math.cos(from.lat) * Math.cos(to.lat) * + Math.sin((from.lon - to.lon)/2) ** 2 + + Math.sin((from.lat - to.lat)/2) ** 2 )) end puts "To gather the desired data, please adjust the following line of Javascript, then run at the appropriate page:" puts %`copy(JSON.stringify($(".sortable tbody tr").get().map(row => [row.cells[], row.cells[]].map(e => e.textContent))))` -dests = JSON.parse(gets).map.with_index do |row, ix| +input = gets +if(input.strip == "[") + loop{line = gets; input += line; break if line.strip == "]"} +end + +dests = JSON.parse(input, symbolize_names: true).map.with_index do |row, ix| row = [row, 1] if row.is_a?(String) row = {name: row[0], size: row[1]} if row.is_a?(Array) size = size.tr("^0-9.", "").to_f if size.is_a?(String) if row[:lat].nil? && USE_GEO - print "#{row[:name]} east latitude? (D M S.S or D M.M or D.D) " + print "#{row[:name]} north latitude? (D M S.S or D M.M or D.D) " row[:lat] = gets end if row[:lon].nil? && USE_GEO - print "#{row[:name]} north longitude? (D M S.S or D M.M or D.D) " + print "#{row[:name]} east longitude? (D M S.S or D M.M or D.D) " row[:lon] = gets end - row[:lat] = dms_to_radians row[:lat] - row[:lon] = dms_to_radians row[:lon] + if USE_GEO + row[:lat] = dms_to_radians row[:lat] + row[:lon] = dms_to_radians row[:lon] + end Dest.new(ix, row[:name], row[:size], 0r, 0r, row[:lat], row[:lon]) end + +last_dest = dests[0] + pairs = PairEnum.new dests if USE_LENGTHS pair_lengths = Array.new(dests.size){|i|Array.new(dests.size){|j| i == j ? 0 : 40000}} + path_next = Array.new(dests.size){|i|Array.new(dests.size){|j| j}} end + + puts "#{dests.size} destinations loaded, resulting in #{pairs.count} pairs." until pairs.empty? - if USE_LENGTHS - pair = pairs.reduce do |a, d| - pair_lengths[a[0].ix][a[1].ix] / geo_dist(*a) < pair_lengths[d[0].ix][d[1].ix] / geo_dist(*d) ? d : a + if USE_LENGTHS || USE_GEO + pair = pairs.max_by do |pair| + [ + CONTINUITY && pair[0] == last_dest ? 1 : 0, + (USE_LENGTHS ? pair_lengths[pair[0].ix][pair[1].ix] : 1.0) / + (pairs.exclusions.map{|i, j| + pair_lengths[i.ix][j.ix] - pair_lengths[i.ix][pair[0].ix] - pair_lengths[pair[1].ix][j.ix] + } + [geo_dist(*pair)]).max, # descending by min length guaranteed by triangle inequality + USE_GEO ? 0 : rand + ] end - elsif USE_GEO - pair = pairs.reduce{|a, d| geo_dist(*a) > geo_dist(*d) ? d : a} else pair = pairs.weighted_sample do |x, y| x.size * (x.indegree + 1) / (x.outdegree + 1) ** 2 * @@ -95,18 +130,27 @@ def geo_dist(from, to) pair[0].outdegree += 1 pair[1].indegree += 1 pairs.delete pair + last_dest = pair[1] geo_dist_str = USE_GEO ? " (distance #{geo_dist(*pair)} km)" : "" - length_str = USE_LENGTHS ? "(shortest path #{pair_lengths[pair[0].ix][pair[1].ix]} min)" : "" - puts "#{pair[0]} => #{pair[1]}#{geo_dist_str}#{length_str}" + length_str = if USE_LENGTHS + path_ixes = [pair[0].ix]; + path_ixes << path_next[path_ixes.last][pair[1].ix] until path_ixes.last == pair[1].ix + via_str = path_ixes.length > 2 ? " via #{path_ixes[1..-2].map{|ix| dests[ix].name}.join(", ")}" : "" + "(shortest path #{pair_lengths[pair[0].ix][pair[1].ix].to_f} km#{via_str})" + else "" + end + puts "from: #{pair[0]} to: #{pair[1]}#{geo_dist_str}#{length_str}" if USE_LENGTHS - print "path length? (min) " - len = gets.to_f + print "path length? (km) " + len_pair = gets.to_r (0...dests.size).each{|i|(0...dests.size).each{|j| - pair_lengths[i][j] = [ - pair_lengths[i][j], - pair_lengths[i][pair[0].ix] + len + pair_lengths[pair[1].ix][j] - ].min + len_ij = pair_lengths[i][pair[0].ix] + len_pair + pair_lengths[pair[1].ix][j] + if pair_lengths[i][j] > len_ij + puts "path from #{dests[i].name} to #{dests[j].name} shortened from #{pair_lengths[i][j].to_f} to #{len_ij.to_f}" + pair_lengths[i][j] = len_ij + path_next[i][j] = i == pair[0].ix ? pair[1].ix : path_next[i][pair[0].ix] + end }} end