Skip to content

Instantly share code, notes, and snippets.

@denim2x
Last active September 5, 2019 18:36
Show Gist options
  • Save denim2x/713f9f46c9e9ec72679a97d2135a58f9 to your computer and use it in GitHub Desktop.
Save denim2x/713f9f46c9e9ec72679a97d2135a58f9 to your computer and use it in GitHub Desktop.
JavaScript code golfing
/**
* Given a string, print the count of vowels in it.
* To add a minor complication, y should be counted as half (0.5) a vowel.
*/
const Vowel = new Set('aeiou'), array = Array.from(input.toLowerCase());
let y = 0;
const vowels = array.filter((c) => {
y += (c == 'y');
return Vowel.has(c)
});
print(vowels.length + y/2);
/**
* Given a string `str`, print the minimum number of character deletions such that
* all same-character subsequences are reduced to a single character.
*/
let ret = 0;
str.replace(/(.)\1+/g, (s) => {
ret += s.length - 1;
});
print(ret);
/**
* Given two string `a` and `b` as input, print the minimum number of character deletions
* across both strings, in order to make each of them the other's anagram.
*
* Sample input:
* fcrxzwscanmligyxyvym
* jxwtrhvujlmrpdoqbisbwhmgpmeoke
*
* Sample output:
* 30
*/
function anagram_dels(a, b) {
const x = new MultiSet(a);
return x.sim_diff(b).size;
}
class MultiSet {
constructor(src) {
if (src instanceof MultiSet) {
this._data = new Map(src._data);
} else {
this._data = new Map;
this._union(src);
}
}
sim_diff(other) {
const m = MultiSet._from(other);
const A = this.diff(m);
const B = m.diff(this);
return A._union(B);
}
diff(other) {
let ret = new MultiSet(this);
for (let e of other) {
ret.delete(e);
}
return ret;
}
union(other) {
return new MultiSet(this)._union(other);
}
_union(other) {
for (let e of _iter(other)) {
this.add(e);
}
return this;
}
has(item) {
return this._data.has(item);
}
add(item) {
const m = this._data;
m.set(item, (m.get(item) || 0) + 1);
return this;
}
delete(item) {
const m = this._data;
if (m.has(item)) {
m.set(item, m.get(item) - 1);
if (m.get(item) == 0) {
m.delete(item);
}
return true;
}
return false;
}
* [Symbol.iterator]() {
for (let [e, n] of this._data) {
for (let i = 0; i < n; i++) {
yield e;
}
}
}
get size() {
let ret = 0;
for (let n of this._data.values()) {
ret += n;
}
return ret;
}
static _from(self) {
if (self instanceof MultiSet)
return self;
return new MultiSet(self);
}
get repr() {
return `{${[...this].join(' ')}}`;
}
}
function* _iter(self) {
if (self != null) {
yield* self;
}
}
print(anagram_dels(a, b));
/**
Print a size ascending range of Christmas trees using asterisks, ranging from size 3 to size 9, each tree separated by a blank line.
A size 3 tree should look like this, with a single centered asterisk for the trunk:
*
***
*****
*
*/
for (let n = 3; n <= 9; n++) {
drawTree(n);
print();
}
function drawTree(n) {
for (let d = 0; d < n; d++) {
let k = n - d;
for (let i = 0; i < k; i++) {
write(' ');
}
for (let i = k; i < k + d*2 + 1; i++) {
write('*');
}
print();
}
for (let i = 0; i < n; i++) {
write(' ');
}
print('*');
}
/**
* Given source code as input, print all its contained line and block comments, each on a separate line.
*/
let cache = [];
let temp = input.replace(/\/\*[\S\s]*?\*\//gi, (c) => {
cache.push(c.replace(/\s*([\n\r])\s*/gi, (m, n) => n));
return '\0';
}).split('\0');
for (let [i, part] of temp.entries()) {
part.replace(/\/\/.*/gi, (c) => {
print(c);
});
if (i < cache.length) {
print(cache[i]);
}
}
/**
* Given a time span as input, print the number of minutes within that time span.
* The time span will be two times separated by a dash, including hour, a colon, minute, and "am" or "pm",
* e.g.: 12:01am-11:59pm. All spans are moving forward in time.
*/
const times = /^\s*(\d+)\s*:\s*(\d+)\s*(am|pm)\s*-\s*(\d+)\s*:\s*(\d+)\s*(am|pm)\s*$/.exec(input);
const int = parseInt;
const A = time_at(0);
const B = time_at(1);
if (A.t == B.t) {
print(B.mins - A.mins);
} else if (A.t != B.t) {
print(B.Mins - A.mins);
}
function time_at(i) {
let k = 3*i;
return { h: int(times[k + 1]), m: int(times[k + 2]), t: times[k + 3] == 'am',
get H() { return this.h + 12 },
get mins() { return this.h*60 + this.m },
get Mins() { return this.H*60 + this.m }
};
}
/**
* Given a string `input` comprised of n sentences (preceded by n) and t words (preceded by t), print
* on separate lines the count of each word within all sentences.
*/
const int = parseInt;
let array = input.split(/\s*[\n\r]+\s*/g);
let n = int(array[0]);
let S = array.slice(1, n+1);
let t = int(array[n+1]);
let W = new Map(array.slice(n+2, t + n+2).map(w => [w, 0]));
let s = S.join(' ');
s.replace(/[a-z1-9_]+/gi, (w) => {
if (W.has(w)) {
W.set(w, W.get(w) + 1);
}
});
for (let n of W.values()) {
print(n);
}
/**
* A number is a divisor of another number if it can divide into it with no remainder.
* Print the positive divisors of each number from 1 to 100 inclusive, on their own line,
* with each divisor separated by a space.
*/
for (let i = 1; i <= 100; i++) {
print(getDivisors(i, ' '));
}
function getDivisors(n, sep) {
let ret = [1];
for (let d = 2; d <= n/2; d++) {
if (n % d == 0) {
ret.push(d);
}
}
n > 1 && ret.push(n);
return sep != null ? ret.join(sep) : ret;
}
/**
* Given a block of text as input, print all domain names found within the text, sorted
* and separated by ';'.
*/
let cache = new Set;
input.replace(/\bhttps?:\/\/(?:ww[w2]\.)?([^\/\s\?"\\']+(?:\.[^\/\s\?"\\']+)+)/gi, (e, d) => {
cache.add(d);
});
print(Array.from(cache).sort().join(';'));
/**
* Given a block of text as input, print all unique e-mail adrresses found within the text, sorted
* and separated by ';'.
*/
let cache = new Set;
input.replace(/\b[^@\s]+@[^@\s]+\.[a-z]{1,3}\b/gi, (e) => {
cache.add(e);
});
print(Array.from(cache).sort().join(';'));
/**
* Given a word as input, print true of the word is a palindrome, or false otherwise.
*/
print(input == Array.from(input).reverse().join(''));
Function.prototype.partial = function (...args) {
return this.bind(null, ...args);
};
const suits = [
['🂱 🂲 🂳 🂴 🂵 🂶 🂷 🂸 🂹 🂺', '🂻 🂼 🂽 🂾'],
['🂡 🂢 🂣 🂤 🂥 🂦 🂧 🂨 🂩 🂪', '🂫 🂬 🂭 🂮'],
['🃁 🃂 🃃 🃄 🃅 🃆 🃇 🃈 🃉 🃊', '🃋 🃌 🃍 🃎'],
['🃑 🃒 🃓 🃔 🃕 🃖 🃗 🃘 🃙 🃚', '🃛 🃜 🃝 🃞'],
];
const model = suits.map(([low, high]) => {
let A;
[A, ...low] = low.split(' ');
high = [...high.split(' '), A];
let cards = new Map([...low, ...high].map((e, i) => [e, 2+i]));
cards.high = new Map(high.map((e, i) => [11+i, 'JCQKA'[i]]));
return cards;
});
function royal_flush(hand) {
for (let temp of hand) {
if (temp.includes())
continue;
temp.sort(numeric);
return temp.every((e, i) => e == i + 10);
}
return false;
}
function straight_flush(hand) {
for (let temp of hand) {
if (temp.includes())
continue;
temp.sort(numeric);
return temp.every(sequenced);
}
return false;
}
var four_alike = _alike.partial(4);
var full_house = freq_match.partial('2,3');
function flush(hand) {
for (let temp of hand) {
if (temp.includes())
continue;
return true;
}
return false;
}
function straight(hand) {
let cache = [];
for (let i = 0; i < hand.count; i++) {
cache.push(hand.find(e => e[i])[i]);
}
cache.sort(numeric);
return cache.every(sequenced);
}
var three_alike = _alike.partial(3);
var two_pair = freq_match.partial('1,2,2');
var pair = _alike.partial(2);
for (let hand of arguments) {
[, ...hand] = hand.match(/^(..)(..)(..)(..)(..)$/);
switch (true) {
case royal_flush(res()):
print('Royal Flush');
break;
case straight_flush(res()):
print('Straight Flush');
break;
case four_alike(res()):
print('Four of a Kind');
break;
case full_house(res()):
print('Full House');
break;
case flush(res()):
print('Flush');
break;
case straight(res()):
print('Straight');
break;
case three_alike(res()):
print('Three of a Kind');
break;
case two_pair(res()):
print('Two Pair');
break;
case pair(res()):
print('Pair');
break;
default:
print('High Card');
}
function res() {
let ret = model.map(suit => {
let ret = hand.map(c => suit.get(c));
ret.high = suit.high;
return ret;
});
ret.count = hand.length;
return ret;
}
}
function _alike(count, hand) {
let freq = new Map;
for (let i = 0; i < hand.count; i++) {
let c = hand.find(e => e[i])[i];
freq.set(c, (freq.get(c) || 0) + 1);
if (freq.get(c) == count)
return true;
}
return false;
}
function freq_match(pat, hand) {
let freq = new Map;
for (let i = 0; i < hand.count; i++) {
let c = hand.find(e => e[i])[i];
freq.set(c, (freq.get(c) || 0) + 1);
}
let vals = [...freq.values()];
return vals.sort(numeric).join() == pat;
}
function sequenced(e, i, a) {
return e == a[0] + i;
}
function numeric(a, b) {
return a - b;
}
/**
* Given input with a number between 0 and 99 in English, print the integer equal to the number.
*/
let output;
const r = String.raw;
let units_a = _`one two three four five`;
let units_b = _`six seven eight nine`;
let units_c = units_b.replace('eight', 'eigh');
let tens_a = _`thir four fif`;
let tens_b = tens_a.replace('four', 'for');
let unit = r`${units_a}|${units_b}`;
let _teen = r`${_`ten eleven twelve`}|(?:${tens_a}|${units_c})teen`;
let _ty_ = r`(?:(?:(twen)|${tens_b}|${units_c})ty)?\s*(?:${unit})?`;
if (input == 'zero') {
output = 0;
} else {
let m = RE(_teen);
if (m) {
output = 10 + m.findIndex(Boolean);
} else {
m = RE(_ty_);
if (m) {
let tens = m.slice(0, 8).findIndex(Boolean);
let units = m.slice(8).findIndex(Boolean);
output = 0;
if (tens >= 0) {
output = 10 * (2 + tens);
}
output += 1 + units;
} else {
output = NaN;
}
}
}
function RE(pat) {
let re = new RegExp(r`^(?:${pat})$`, 'i');
let m = re.exec(input.trim());
return m ? m.slice(1) : null;
}
function _([s]) {
return s.split(/\s+/g).map(e => `(${e})`).join('|');
}
print(output);
/**
* Given a list of pairs (in arguments), print on separate lines the relationship between each pair's components.
*
* ✂ cuts 📄 covers 💎 crushes 🦎 poisons 🖖 smashes ✂ decapitates 🦎 eats 📄 disproves 🖖 vaporizes 💎 crushes ✂.
*/
const model = {
Rock: { Lizard: 'crushes', Scissors: 'crushes', $: '💎' },
Paper: { Rock: 'covers', Spock: 'disproves', $: '📄' },
Scissors: { Paper: 'cuts', Lizard: 'decapitates', $: '✂' },
Spock: { Scissors: 'smashes', Rock: 'vaporizes', $: '🖖' },
Lizard: { Spock: 'poisons', Paper: 'eats', $: '🦎' },
};
const chars = new Map;
for (let [a, { $: c }] of Object.entries(model)) {
chars.set(a, c);
}
const rels = new Map;
for (let [k, B] of Object.entries(model)) {
let a = chars.get(k);
rels.set(`${a}${a}`, 'Tie');
for (let [k, r] of Object.entries(B)) {
let b = chars.get(k);
let val = `${a} ${r} ${b}`;
rels.set(`${a}${b}`, val);
rels.set(`${b}${a}`, val);
}
}
for (let pair of arguments) {
print(rels.get(pair));
}
/**
* Given an integer (0-99) as input, print it's spelling in English
*/
let output;
const tens_a = `thir four fif`;
const tens_b = tens_a.replace('four', 'for');
const words_b = 'six seven eight nine');
const words_c = words_b.replace('eight', 'eigh'));
const words = _`zero one two three four five ${words_b} ten eleven twelve`;
const words_teen = _`${tens_a} ${words_c}`;
const words_ty = _`twen ${tens_b} ${words_c}`;
if (0 <= input && input <= 99) {
let tens = Math.floor(input / 10);
let units = input % 10;
if (input > 19) {
output = `${words_ty[tens-2]}ty`;
if (units > 0) {
output = `${output} ${words[units]}`;
}
} else if (input > 12) {
output = `${words_teen[input-13]}teen`;
} else {
output = words[input];
}
}
function _([s]) {
return s.split(/\s+/g);
}
print(output);
// For three given arrays A, B, C, find the triplet A[i], B[j], C[k] such that A[i] < B[j] < C[k]
let res;
for (let i = 0; i < A.length && !res; i++) {
for (let j = 0; j < B.length && !res; j++) {
for (let k = 0; k < C.length; k++) {
if (A[i] < B[j] && B[j] < C[k]) {
res = [i, j, k];
break;
}
}
}
}
print(...res);
/**
* Given a `grid` of letters and a list of `words`, print the starting (1-indexed) position for each
* of these words within the grid, along all eight directions.
*
* Sample `grid`:
* abcDEFGhigg
* hEbkWalDork
* FtyAwaldORm
* FtsimrLqsrc
* byoArBeDeyv
* Klcbqwikomk
* strEBGadhrb
* yUiqlxcnBjf
*
* Sample `words`:
* Waldorf
* Bambi
* Betty
* Dagbert
*
* Output:
* 2 5
* 2 3
* 1 2
* 7 8
*/
function* search(grid, words) {
const fwd = new Map, rev = new Map;
for (let w of words) {
fwd.set(w, new Cache(w));
rev.set(w, new Cache(w, true));
}
for (let y = 0; y < grid.length; y++) {
let row = grid[y];
for (let x = 0; x < row.length; x++) {
let c = row[x];
for (let [w, cache] of fwd) {
cache.add(c, x, y);
}
for (let [w, cache] of rev) {
cache.add(c, x, y);
}
}
}
for (let w of words) {
yield fwd.get(w).match || rev.get(w).match;
}
}
class Cache {
constructor(word, reversed=false) {
this._word = word.toLowerCase();
this._reversed = reversed;
if (this._reversed) {
this._word = reverse(this._word);
}
this._vectors = [];
this._matches = [];
}
add(char, x, y) {
if (this.match)
return false;
let c = char.toLowerCase(), ok = false;
let cell = new Cell(x, y);
for (let v of this._vectors) {
let k = v.length;
let end = v[k-1];
if (v.dir && !v.dir.same_direction(end, cell))
continue;
if (this._word[k] != c)
continue;
v.dir = end.direction(cell);
if (v.dir == null)
continue;
ok = k == 1 ? [end] : (ok || true);
v.push(cell);
if (v.length == this._word.length) {
this._matches.push(this._reversed ? cell : v[0]);
}
}
if (Array.isArray(ok)) {
this._vectors.push(ok);
} else if (!ok && this._word[0] == c) {
this._vectors.push([cell]);
}
return true;
}
* [Symbol.iterator]() {
yield* this._matches;
}
get match() {
return this._matches[0];
}
get size() {
return this._matches.length;
}
}
class Cell {
constructor(x, y) {
this._x = x;
this._y = y;
}
direction(dest) {
let ret = dest.sub(this);
let { x, y } = ret;
if (x == 1 && y == 0)
return ret;
if (Math.abs(x) <= 1 && y == 1)
return ret;
}
sub({ x, y }) {
return new Cell(this.x - x, this.y - y);
}
same_direction(a, b) {
// 'this' is the direction
return this.equals(a.direction(b));
}
equals(other) {
if (other == null)
return false;
return this.x == other.x && this.y == other.y;
}
get x() {
return this._x;
}
get y() {
return this._y;
}
get repr() {
return `(${this.x}, ${this.y})`;
}
}
function reverse(str) {
return Array.from(str).reverse().join('');
}
for (let {x, y} of search(grid, words)) {
print(y + 1, x + 1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment