From 77bcd2b2f2fcb3c712e6d80949d44a88b22d0794 Mon Sep 17 00:00:00 2001 From: Simponic Date: Thu, 23 Feb 2023 10:15:07 -0700 Subject: [PATCH 1/5] Initial euler golf sol --- euler-golf/index.html | 38 ++++++ euler-golf/js/cx.js | 308 ++++++++++++++++++++++++++++++++++++++++++ euler-golf/js/game.js | 11 ++ euler-golf/js/sol.js | 44 ++++++ index.html | 88 +++++++----- 5 files changed, 456 insertions(+), 33 deletions(-) create mode 100644 euler-golf/index.html create mode 100644 euler-golf/js/cx.js create mode 100644 euler-golf/js/game.js create mode 100644 euler-golf/js/sol.js diff --git a/euler-golf/index.html b/euler-golf/index.html new file mode 100644 index 0000000..2682a0c --- /dev/null +++ b/euler-golf/index.html @@ -0,0 +1,38 @@ + + + + Euler Golf 2 + + + + + + + + + + diff --git a/euler-golf/js/cx.js b/euler-golf/js/cx.js new file mode 100644 index 0000000..371415d --- /dev/null +++ b/euler-golf/js/cx.js @@ -0,0 +1,308 @@ +// http://www.russellcottrell.com/fractalsEtc/cx.js + +class cx { + static degrees(d) { + cx._RD = d ? Math.PI / 180 : 1; + } + // Math.PI/180 for degrees, 1 for radians + // applies to i/o (constructor, get/set arg, and toString etc.) + + constructor(x, y, polar) { + if (!polar) { + this.re = x; + this.im = y; + } else { + y *= cx._RD; // may be radians or degrees + this.re = x * Math.cos(y); + this.im = x * Math.sin(y); + } + } + + get abs() { + return Math.sqrt(this.re * this.re + this.im * this.im); + } + + set abs(r) { + var theta = this._arg; + this.re = r * Math.cos(theta); + this.im = r * Math.sin(theta); + } + + get arg() { + // returns radians or degrees, non-negative + return ( + ((Math.atan2(this.im, this.re) + 2 * Math.PI) % (2 * Math.PI)) / cx._RD + ); + } + + set arg(theta) { + // may be radians or degrees + var r = this.abs; + this.re = r * Math.cos(theta * cx._RD); + this.im = r * Math.sin(theta * cx._RD); + } + + get _arg() { + // internal; returns radians + return Math.atan2(this.im, this.re); + } + + static get i() { + return new cx(0, 1); + } + + static set i(x) { + throw new Error("i is read-only"); + } + + toString(polar) { + if (!polar) + return ( + this.re.toString() + + (this.im >= 0 ? " + " : " - ") + + Math.abs(this.im).toString() + + "i" + ); + else return this.abs.toString() + " cis " + this.arg.toString(); + } + + toPrecision(n, polar) { + if (!polar) + return ( + this.re.toPrecision(n) + + (this.im >= 0 ? " + " : " - ") + + Math.abs(this.im).toPrecision(n) + + "i" + ); + else return this.abs.toPrecision(n) + " cis " + this.arg.toPrecision(n); + } + + toPrecis(n, polar) { + // trims trailing zeros + if (!polar) + return ( + parseFloat(this.re.toPrecision(n)).toString() + + (this.im >= 0 ? " + " : " - ") + + parseFloat(Math.abs(this.im).toPrecision(n)).toString() + + "i" + ); + else + return ( + parseFloat(this.abs.toPrecision(n)).toString() + + " cis " + + parseFloat(this.arg.toPrecision(n)).toString() + ); + } + + toFixed(n, polar) { + if (!polar) + return ( + this.re.toFixed(n) + + (this.im >= 0 ? " + " : " - ") + + Math.abs(this.im).toFixed(n) + + "i" + ); + else return this.abs.toFixed(n) + " cis " + this.arg.toFixed(n); + } + + toExponential(n, polar) { + if (!polar) + return ( + this.re.toExponential(n) + + (this.im >= 0 ? " + " : " - ") + + Math.abs(this.im).toExponential(n) + + "i" + ); + else return this.abs.toExponential(n) + " cis " + this.arg.toExponential(n); + } + + static getReals(c, d) { + // when c or d may be simple or complex + var x, y, u, v; + if (c instanceof cx) { + x = c.re; + y = c.im; + } else { + x = c; + y = 0; + } + if (d instanceof cx) { + u = d.re; + v = d.im; + } else { + u = d; + v = 0; + } + return [x, y, u, v]; + } + + static conj(c) { + return new cx(c.re, -c.im); + } + + static neg(c) { + return new cx(-c.re, -c.im); + } + + static add(c, d) { + var a = cx.getReals(c, d); + var x = a[0]; + var y = a[1]; + var u = a[2]; + var v = a[3]; + return new cx(x + u, y + v); + } + + static sub(c, d) { + var a = cx.getReals(c, d); + var x = a[0]; + var y = a[1]; + var u = a[2]; + var v = a[3]; + return new cx(x - u, y - v); + } + + static mult(c, d) { + var a = cx.getReals(c, d); + var x = a[0]; + var y = a[1]; + var u = a[2]; + var v = a[3]; + return new cx(x * u - y * v, x * v + y * u); + } + + static div(c, d) { + var a = cx.getReals(c, d); + var x = a[0]; + var y = a[1]; + var u = a[2]; + var v = a[3]; + return new cx( + (x * u + y * v) / (u * u + v * v), + (y * u - x * v) / (u * u + v * v) + ); + } + + static pow(c, int) { + if (Number.isInteger(int) && int >= 0) { + var r = Math.pow(c.abs, int); + var theta = int * c._arg; + return new cx(r * Math.cos(theta), r * Math.sin(theta)); + } else return NaN; + } + + static root(c, int, k) { + if (!k) k = 0; + if ( + Number.isInteger(int) && + int >= 2 && + Number.isInteger(k) && + k >= 0 && + k < int + ) { + var r = Math.pow(c.abs, 1 / int); + var theta = (c._arg + 2 * k * Math.PI) / int; + return new cx(r * Math.cos(theta), r * Math.sin(theta)); + } else return NaN; + } + + static log(c) { + return new cx(Math.log(c.abs), c._arg); + } + + static exp(c) { + var r = Math.exp(c.re); + var theta = c.im; + return new cx(r * Math.cos(theta), r * Math.sin(theta)); + } + + static sin(c) { + var a = c.re; + var b = c.im; + return new cx(Math.sin(a) * Math.cosh(b), Math.cos(a) * Math.sinh(b)); + } + + static cos(c) { + var a = c.re; + var b = c.im; + return new cx(Math.cos(a) * Math.cosh(b), -Math.sin(a) * Math.sinh(b)); + } + + static tan(c) { + return cx.div(cx.sin(c), cx.cos(c)); + } + + static asin(c, k) { + if (!k) k = 0; + var ic = cx.mult(cx.i, c); + var c2 = cx.pow(c, 2); + return cx.mult( + cx.neg(cx.i), + cx.log(cx.add(ic, cx.root(cx.sub(1, c2), 2, k))) + ); + } + + static acos(c, k) { + if (!k) k = 0; + var c2 = cx.pow(c, 2); + return cx.mult( + cx.neg(cx.i), + cx.log(cx.add(c, cx.mult(cx.i, cx.root(cx.sub(1, c2), 2, k)))) + ); + } + + static atan(c) { + return cx.mult( + cx.div(cx.i, 2), + cx.log(cx.div(cx.add(cx.i, c), cx.sub(cx.i, c))) + ); + } + + static sinh(c) { + var a = c.re; + var b = c.im; + return new cx(Math.sinh(a) * Math.cos(b), Math.cosh(a) * Math.sin(b)); + } + + static cosh(c) { + var a = c.re; + var b = c.im; + return new cx(Math.cosh(a) * Math.cos(b), Math.sinh(a) * Math.sin(b)); + } + + static tanh(c) { + return cx.div(cx.sinh(c), cx.cosh(c)); + } + + static asinh(c, k) { + if (!k) k = 0; + var c2 = cx.pow(c, 2); + return cx.log(cx.add(c, cx.root(cx.add(c2, 1), 2, k))); + } + + static acosh(c, k) { + if (!k) k = 0; + var c2 = cx.pow(c, 2); + return cx.log(cx.add(c, cx.root(cx.sub(c2, 1), 2, k))); + } + + static atanh(c) { + return cx.mult(cx.div(1, 2), cx.log(cx.div(cx.add(1, c), cx.sub(1, c)))); + } + + static copy(c) { + return new cx(c.re, c.im); + } + + static eq(c, d, epsilon) { + if (!epsilon) { + if (c.re == d.re && c.im == d.im) return true; + } else { + if (Math.abs(c.re - d.re) < epsilon && Math.abs(c.im - d.im) < epsilon) + return true; + } + return false; + } +} + +cx.degrees(true); // need to call this diff --git a/euler-golf/js/game.js b/euler-golf/js/game.js new file mode 100644 index 0000000..6283cc4 --- /dev/null +++ b/euler-golf/js/game.js @@ -0,0 +1,11 @@ +const WIDTH = 1000; +const HEIGHT = 1000; + +const draw_grid = (ctx) => {}; + +const main = () => { + const canvas = document.getElementById("canvas"); + const ctx = canvas.getContext("2d"); +}; + +main(); diff --git a/euler-golf/js/sol.js b/euler-golf/js/sol.js new file mode 100644 index 0000000..a022c99 --- /dev/null +++ b/euler-golf/js/sol.js @@ -0,0 +1,44 @@ +const DEPTH = 7; + +const DIRECTION = { + 0: new cx(0, 1), + 1: new cx(0, -1), +}; + +const move = (prev, curr, c) => cx.add(prev, cx.mult(c, cx.sub(curr, prev))); +const construct_moves = (curr, prev) => + Object.keys(DIRECTION).map((x) => move(curr, prev, DIRECTION[x])); +const backtrack = (local_index, depth) => + local_index + .toString(2) + .padStart(depth, "0") + .split("") + .reverse() + .map((dir) => (dir ? "+" : "-")); + +const sol = (target, start_from = new cx(0, 0), start_to = new cx(1, 0)) => { + let moves = construct_moves(start_from, start_to); + let curr_depth = 2; // potential bug: when target is within one move away + + do { + for (let i = 0; i < Math.pow(2, curr_depth); i++) { + const current_i = + (i >> 1) + ((1 - Math.pow(2, curr_depth - 1)) / (1 - 2) - 1); + const previous_i = + (i >> 2) + ((1 - Math.pow(2, curr_depth - 2)) / (1 - 2) - 1); + + const new_move = move( + previous_i < 0 ? start_from : moves[previous_i], + moves[current_i], + DIRECTION[parseInt(i.toString(2)[0])] + ); + + if (cx.eq(new_move, target)) return backtrack(moves, target); + + moves.push(new_move); + } + curr_depth++; + } while (curr_depth < DEPTH); + console.log(moves); + return null; +}; diff --git a/index.html b/index.html index 3eb01e3..758b4e0 100644 --- a/index.html +++ b/index.html @@ -3,45 +3,71 @@ Simponic's Static Sites - + - - - + + +
- +

- ๐Ÿ‘‹ Hello, I'm Simponic! -
- ๐Ÿ“– This page hosts strictly static content. -
- ๐Ÿ”” My "real website" is at simponic.xyz. + ๐Ÿ‘‹ Hello, I'm Simponic! +
+ ๐Ÿ“– This page hosts strictly static content. +
+ ๐Ÿ”” My "real website" is at + simponic.xyz.

-
+
- +
-

DVD Logo Bouncing Animation

-

Brings back the nostalgia of old-school DVD players with an intersection predictor. The twist: no Canvas API! Only svg's and absolute positioned images!

+

Julia Set Explorer

+

+ Zoom, pan, and "c" complex changes in this fun GPU-accelerated + playground! +

+
+
+ +
+ +
+

Discrete Fourier Visualizer

+

+ Draw how your year has gone and view a reactive graph containing + its DFT by dragging your mouse over the canvas! +

+
+
-
+

The A-maze-ing Maize Maze

-

A Randomized Kruskal's Maze game with BFS path-finding. You play as a ๐ŸŒฝcorn stalk trying to become ๐Ÿฟpopcorn.

+

+ A Randomized Kruskal's Maze game with BFS path-finding. You play + as a ๐ŸŒฝcorn stalk trying to become ๐Ÿฟpopcorn. +

@@ -50,31 +76,27 @@
-
+

Centipede

-

In this game, shoot all the centipede bodies and score points and go up levels.

+

+ In this game, shoot all the centipede bodies and score points and + go up levels. +

-
+
- +
-

Julia Set Explorer

-

Zoom, pan, and "c" complex changes in this fun GPU-accelerated playground!

-
-
- -
-
- -
- -
-

Discrete Fourier Visualizer

-

Draw how your year has gone and view a reactive graph containing its DFT by dragging your mouse over the canvas!

+

DVD Logo Bouncing Animation

+

+ Brings back the nostalgia of old-school DVD players with an + intersection predictor. The twist: no Canvas API! Only svg's and + absolute positioned images! +

-- 2.45.2 From f0c0b9853ee025c112cc7a8c56ac044053208b6b Mon Sep 17 00:00:00 2001 From: Lizzy Hunt Date: Thu, 23 Feb 2023 18:18:31 -0700 Subject: [PATCH 2/5] More euler golf --- euler-golf/css/styles.css | 24 +++++++ euler-golf/index.html | 31 ++------- euler-golf/js/game.js | 130 +++++++++++++++++++++++++++++++++++--- euler-golf/js/json-ds.js | 17 +++++ euler-golf/js/sol.js | 35 +++++----- 5 files changed, 183 insertions(+), 54 deletions(-) create mode 100644 euler-golf/css/styles.css create mode 100644 euler-golf/js/json-ds.js diff --git a/euler-golf/css/styles.css b/euler-golf/css/styles.css new file mode 100644 index 0000000..f343898 --- /dev/null +++ b/euler-golf/css/styles.css @@ -0,0 +1,24 @@ +body { + margin: 0; + padding: 0; + overflow: scroll; + font-family: Lucida Console, Lucida Sans Typewriter, monaco, + Bitstream Vera Sans Mono, monospace; +} + +.canvas-holder canvas { + padding: 0; + margin: auto; + display: block; + height: 70vh; + width: auto; + max-width: 100%; + border: 1px solid black; +} + +.button { + border: 1px solid black; + border-radius: 5px; + padding: 5px; + cursor: pointer; +} diff --git a/euler-golf/index.html b/euler-golf/index.html index 2682a0c..2717ed5 100644 --- a/euler-golf/index.html +++ b/euler-golf/index.html @@ -2,36 +2,15 @@ Euler Golf 2 - + - +
+ +
+ diff --git a/euler-golf/js/game.js b/euler-golf/js/game.js index 6283cc4..25c1632 100644 --- a/euler-golf/js/game.js +++ b/euler-golf/js/game.js @@ -1,11 +1,123 @@ -const WIDTH = 1000; -const HEIGHT = 1000; - -const draw_grid = (ctx) => {}; - -const main = () => { - const canvas = document.getElementById("canvas"); - const ctx = canvas.getContext("2d"); +const CANVAS_ID = "myCanvas"; +const DEFAULTS = { + rows: 15, + cols: 15, + grid_padding: 10, }; -main(); +let state = { + ...DEFAULTS, + canvas: document.getElementById("canvas"), + ctx: document.getElementById("canvas").getContext("2d"), + last_render: 0, + changes: { + width: document.body.clientWidth, + height: document.body.clientHeight, + }, +}; + +const rand_between = (min, max) => + Math.floor(Math.random() * (max - min + 1)) + min; + +const rand_target = (cols, rows) => { + const res = new cx(rand_between(0, cols), rand_between(0, rows)); + if (res.re % 2 || res.im % 2) return rand_target(cols, rows); + + return res; +}; + +CanvasRenderingContext2D.prototype.circle = function (x, y, r, color) { + this.beginPath(); + this.arc(x, y, r, 0, Math.PI * 2); + this.fillStyle = color; + this.fill(); + this.closePath(); +}; + +CanvasRenderingContext2D.prototype.line = function ( + { x: x1, y: y1 }, + { x: x2, y: y2 }, + width, + color, + cap = "round" +) { + this.beginPath(); + this.moveTo(x1, y1); + this.lineTo(x2, y2); + this.lineWidth = width; + this.strokeStyle = color; + this.lineCap = cap; + this.stroke(); + this.closePath(); +}; + +CanvasRenderingContext2D.prototype.do_grid = function ( + rows, + cols, + draw_at_grid_pos = (ctx, x, y) => ctx.circle(x, y, 10, "#00ff00") +) { + for (let y = 0; y < rows; y++) { + for (let x = 0; x < cols; x++) { + draw_at_grid_pos(this, x, y); + } + } +}; + +CanvasRenderingContext2D.prototype.cartesian_grid = function ( + rows, + cols, + width, + height, + grid_padding, + circle_spec_at_coords = (x, y) => ({ radius: 5, color: "#000" }) +) { + const center = { x: Math.floor(cols / 2), y: Math.floor(rows / 2) }; + + const dx = (width - 2 * grid_padding) / cols; + const dy = (height - 2 * grid_padding) / rows; + + const start_x = grid_padding + dx / 2; + const start_y = grid_padding + dy / 2; + + this.do_grid(rows, cols, (ctx, x, y) => { + const x_pos = x * dx + start_x; + const y_pos = y * dy + start_y; + const { radius, color } = circle_spec_at_coords(x, y); + + ctx.circle(x_pos, y_pos, radius, color); + }); +}; + +const render = ({ canvas, ctx, rows, cols, grid_padding }) => { + const { width, height } = canvas; + + ctx.clearRect(0, 0, width, height); + ctx.cartesian_grid(rows, cols, width, height, grid_padding, (x, y) => { + if (x == Math.floor(cols / 2) && y == Math.floor(rows / 2)) { + return { + radius: 7, + color: "#0000ff", + }; + } else { + return { + radius: 3, + color: "#000", + }; + } + }); +}; + +const loop = (now) => { + const dt = now - state.last_render; + state.changes.last_render = now; + if (Object.keys(state.changes).length > 0) { + state = { ...state, ...state.changes }; + state.changes = {}; + + render(state); + } + + requestAnimationFrame(loop); +}; + +requestAnimationFrame(loop); diff --git a/euler-golf/js/json-ds.js b/euler-golf/js/json-ds.js new file mode 100644 index 0000000..24a8729 --- /dev/null +++ b/euler-golf/js/json-ds.js @@ -0,0 +1,17 @@ +class JSONSet { + items = new Set(); + constructor(initial) { + if (Array.isArray(initial)) { + initial.map((x) => this.apply_set_function("add", x)); + } else { + this.apply_set_function("add", initial); + } + + ["add", "has", "remove"].forEach( + (f_name) => (this[f_name] = (x) => this.apply_set_function(f_name, x)) + ); + } + apply_set_function(f_name, x) { + return this.items[f_name](JSON.stringify(x)); + } +} diff --git a/euler-golf/js/sol.js b/euler-golf/js/sol.js index a022c99..a8ce2cf 100644 --- a/euler-golf/js/sol.js +++ b/euler-golf/js/sol.js @@ -1,4 +1,4 @@ -const DEPTH = 7; +const DEPTH = 21; const DIRECTION = { 0: new cx(0, 1), @@ -6,39 +6,36 @@ const DIRECTION = { }; const move = (prev, curr, c) => cx.add(prev, cx.mult(c, cx.sub(curr, prev))); + const construct_moves = (curr, prev) => Object.keys(DIRECTION).map((x) => move(curr, prev, DIRECTION[x])); + const backtrack = (local_index, depth) => local_index .toString(2) .padStart(depth, "0") .split("") - .reverse() - .map((dir) => (dir ? "+" : "-")); + .map((direction) => (Number(direction) ? "-" : "+")); const sol = (target, start_from = new cx(0, 0), start_to = new cx(1, 0)) => { - let moves = construct_moves(start_from, start_to); - let curr_depth = 2; // potential bug: when target is within one move away + let moves = [start_to, ...construct_moves(start_from, start_to)]; + let curr_depth = 2; - do { + while (curr_depth < DEPTH) { for (let i = 0; i < Math.pow(2, curr_depth); i++) { - const current_i = - (i >> 1) + ((1 - Math.pow(2, curr_depth - 1)) / (1 - 2) - 1); - const previous_i = - (i >> 2) + ((1 - Math.pow(2, curr_depth - 2)) / (1 - 2) - 1); + const direction = DIRECTION[Number(i.toString(2).at(-1))]; + // Current element is at i >> 1 + the offset for the previous group (which is + // the sum of the geometric series 2**n until curr_depth - 1) + const current_i = (i >> 1) + (1 - Math.pow(2, curr_depth - 1)) / (1 - 2); + const previous_i = (i >> 2) + (1 - Math.pow(2, curr_depth - 2)) / (1 - 2); - const new_move = move( - previous_i < 0 ? start_from : moves[previous_i], - moves[current_i], - DIRECTION[parseInt(i.toString(2)[0])] - ); - - if (cx.eq(new_move, target)) return backtrack(moves, target); + const new_move = move(moves[previous_i], moves[current_i], direction); moves.push(new_move); + if (cx.eq(new_move, target)) return backtrack(i, curr_depth); } curr_depth++; - } while (curr_depth < DEPTH); - console.log(moves); + } + return null; }; -- 2.45.2 From 74cc58b6118cb3bfe6f95edeca90b1df58932843 Mon Sep 17 00:00:00 2001 From: Simponic Date: Fri, 24 Feb 2023 00:53:16 -0700 Subject: [PATCH 3/5] Working euler golf with controls --- euler-golf/css/styles.css | 28 +++++- euler-golf/index.html | 6 +- euler-golf/js/game.js | 190 +++++++++++++++++++++++++++++--------- euler-golf/js/sol.js | 2 - 4 files changed, 172 insertions(+), 54 deletions(-) diff --git a/euler-golf/css/styles.css b/euler-golf/css/styles.css index f343898..05f2f9f 100644 --- a/euler-golf/css/styles.css +++ b/euler-golf/css/styles.css @@ -4,16 +4,24 @@ body { overflow: scroll; font-family: Lucida Console, Lucida Sans Typewriter, monaco, Bitstream Vera Sans Mono, monospace; + width: 100vw; + height: 100vh; + background: rgb(238, 174, 202); + background: radial-gradient( + circle, + rgba(238, 174, 202, 1) 0%, + rgba(148, 187, 233, 1) 100% + ); } -.canvas-holder canvas { +.canvas { padding: 0; margin: auto; display: block; - height: 70vh; - width: auto; - max-width: 100%; border: 1px solid black; + + width: 100vw; + height: 100vw; } .button { @@ -22,3 +30,15 @@ body { padding: 5px; cursor: pointer; } + +.controls { + padding: 12px; + position: fixed; + bottom: 0; + right: 0; + background-color: rgba(255, 255, 255, 0.5); + border: 1px solid white; + border-radius: 8px; + margin-right: 6px; + margin-bottom: 6px; +} diff --git a/euler-golf/index.html b/euler-golf/index.html index 2717ed5..fb31d41 100644 --- a/euler-golf/index.html +++ b/euler-golf/index.html @@ -5,8 +5,10 @@ -
- + + +
+
diff --git a/euler-golf/js/game.js b/euler-golf/js/game.js index 25c1632..4edaa37 100644 --- a/euler-golf/js/game.js +++ b/euler-golf/js/game.js @@ -1,31 +1,23 @@ -const CANVAS_ID = "myCanvas"; const DEFAULTS = { - rows: 15, - cols: 15, - grid_padding: 10, + max_rows: 80, + max_cols: 80, + min_gap: 40, + angle_multiplier: 0.0005, }; +const CANVAS = document.getElementById("canvas"); let state = { - ...DEFAULTS, - canvas: document.getElementById("canvas"), - ctx: document.getElementById("canvas").getContext("2d"), + grid_padding: 10, + canvas: CANVAS, + ctx: CANVAS.getContext("2d"), last_render: 0, - changes: { - width: document.body.clientWidth, - height: document.body.clientHeight, - }, -}; - -const rand_between = (min, max) => - Math.floor(Math.random() * (max - min + 1)) + min; - -const rand_target = (cols, rows) => { - const res = new cx(rand_between(0, cols), rand_between(0, rows)); - if (res.re % 2 || res.im % 2) return rand_target(cols, rows); - - return res; + path: [new cx(0, 0), new cx(1, 0)], + angle: new cx(0, 0), + keys: {}, + changes: {}, }; +// Rendering CanvasRenderingContext2D.prototype.circle = function (x, y, r, color) { this.beginPath(); this.arc(x, y, r, 0, Math.PI * 2); @@ -35,22 +27,35 @@ CanvasRenderingContext2D.prototype.circle = function (x, y, r, color) { }; CanvasRenderingContext2D.prototype.line = function ( - { x: x1, y: y1 }, - { x: x2, y: y2 }, + { x_pos: x1, y_pos: y1 }, + { x_pos: x2, y_pos: y2 }, width, color, cap = "round" ) { - this.beginPath(); - this.moveTo(x1, y1); - this.lineTo(x2, y2); this.lineWidth = width; this.strokeStyle = color; this.lineCap = cap; + + this.beginPath(); + this.moveTo(x1, y1); + this.lineTo(x2, y2); this.stroke(); this.closePath(); }; +CanvasRenderingContext2D.prototype.draw_cartesian_path = function ( + grid_spec, + cartesian_path, + width = 2, + color = "#000" +) { + const path = cartesian_path.map((coord) => grid_to_canvas(coord, grid_spec)); + path.slice(1).forEach((coord, i) => { + this.line(path[i], coord, width, color); + }); +}; + CanvasRenderingContext2D.prototype.do_grid = function ( rows, cols, @@ -66,33 +71,92 @@ CanvasRenderingContext2D.prototype.do_grid = function ( CanvasRenderingContext2D.prototype.cartesian_grid = function ( rows, cols, - width, - height, - grid_padding, - circle_spec_at_coords = (x, y) => ({ radius: 5, color: "#000" }) + grid_spec, + circle_spec_at_coords = (_x, _y) => ({ radius: 5, color: "#000" }) ) { - const center = { x: Math.floor(cols / 2), y: Math.floor(rows / 2) }; - - const dx = (width - 2 * grid_padding) / cols; - const dy = (height - 2 * grid_padding) / rows; - - const start_x = grid_padding + dx / 2; - const start_y = grid_padding + dy / 2; - this.do_grid(rows, cols, (ctx, x, y) => { - const x_pos = x * dx + start_x; - const y_pos = y * dy + start_y; + const { x_pos, y_pos } = grid_to_canvas({ x, y }, grid_spec); const { radius, color } = circle_spec_at_coords(x, y); ctx.circle(x_pos, y_pos, radius, color); }); }; -const render = ({ canvas, ctx, rows, cols, grid_padding }) => { - const { width, height } = canvas; +// Utilities +const move = (prev, curr, c) => cx.add(prev, cx.mult(c, cx.sub(curr, prev))); +const rand_between = (min, max) => + Math.floor(Math.random() * (max - min + 1)) + min; + +const rand_target = (cols, rows) => { + const res = new cx(rand_between(0, cols), rand_between(0, rows)); + if (res.re % 2 || res.im % 2) return rand_target(cols, rows); + + return res; +}; + +const calculate_grid_spec = ({ rows, cols, width, height, grid_padding }) => { + const dx = (width - 2 * grid_padding) / cols; + const dy = (height - 2 * grid_padding) / rows; + + return { + dx, + dy, + start_x: grid_padding + dx / 2, + start_y: grid_padding + dy / 2, + }; +}; + +const grid_to_canvas = ({ x, y }, { dx, dy, start_x, start_y }) => ({ + x_pos: x * dx + start_x, + y_pos: y * dy + start_y, +}); + +const complex_to_grid = (c, rows, cols) => { + const { re, im } = c; + return { + x: re + Math.floor(cols / 2), + y: Math.floor(rows / 2) - im, + }; +}; + +// Game loop +const handle_input = (state, dt) => { + if (state.keys.ArrowLeft) { + state.angle.im += DEFAULTS.angle_multiplier * dt; + } else if (state.keys.ArrowRight) { + state.angle.im -= DEFAULTS.angle_multiplier * dt; + } + + if (state.angle.im <= -1 || state.angle.im >= 1) { + state.angle.im = state.angle.im <= -1 ? -1 : 1; + state.path.push(move(state.path.at(-2), state.path.at(-1), state.angle)); + state.angle = new cx(0, 0); + } +}; + +const render = ({ width, height, ctx, rows, cols } = state) => { ctx.clearRect(0, 0, width, height); - ctx.cartesian_grid(rows, cols, width, height, grid_padding, (x, y) => { + ctx.fillStyle = "rgba(255, 255, 255, 0)"; + ctx.fillRect(0, 0, width, height); + + const grid_spec = calculate_grid_spec(state); + + const curr = state.path.at(-1); + const prev = state.path.at(-2); + + const v_diff = cx.sub(curr, prev); + const theta = (state.angle.im * Math.PI) / 2; + + const angle_re = Math.cos(theta) * v_diff.re - Math.sin(theta) * v_diff.im; + const angle_im = Math.sin(theta) * v_diff.re + Math.cos(theta) * v_diff.im; + + ctx.draw_cartesian_path(grid_spec, [ + ...state.path.map((c) => complex_to_grid(c, rows, cols)), + complex_to_grid(cx.add(new cx(angle_re, angle_im), prev), rows, cols), + ]); + + ctx.cartesian_grid(rows, cols, grid_spec, (x, y) => { if (x == Math.floor(cols / 2) && y == Math.floor(rows / 2)) { return { radius: 7, @@ -101,7 +165,7 @@ const render = ({ canvas, ctx, rows, cols, grid_padding }) => { } else { return { radius: 3, - color: "#000", + color: `rgb(${255 * (x / cols)}, 100, 100)`, // todo: animate with last_render }; } }); @@ -110,14 +174,48 @@ const render = ({ canvas, ctx, rows, cols, grid_padding }) => { const loop = (now) => { const dt = now - state.last_render; state.changes.last_render = now; + if (Object.keys(state.changes).length > 0) { + if (state.changes.width || state.changes.height) { + state.changes.rows = Math.min( + DEFAULTS.max_rows, + state.changes.height / DEFAULTS.min_gap + ); + state.changes.cols = Math.min( + DEFAULTS.max_cols, + state.changes.width / DEFAULTS.min_gap + ); + } + state = { ...state, ...state.changes }; state.changes = {}; - - render(state); } + handle_input(state, dt); + render(state); requestAnimationFrame(loop); }; +// DOM +const on_resize = () => { + CANVAS.width = document.body.clientWidth; + CANVAS.height = document.body.clientHeight; + state.changes.width = CANVAS.width; + state.changes.height = CANVAS.height; +}; + +const on_keyup = (e) => { + delete state.keys[e.key]; +}; + +const on_keydown = (e) => { + state.keys[e.key] = true; +}; + +window.addEventListener("resize", on_resize); +window.addEventListener("keydown", on_keydown); +window.addEventListener("keyup", on_keyup); + +// main +on_resize(); requestAnimationFrame(loop); diff --git a/euler-golf/js/sol.js b/euler-golf/js/sol.js index a8ce2cf..be4a9aa 100644 --- a/euler-golf/js/sol.js +++ b/euler-golf/js/sol.js @@ -5,8 +5,6 @@ const DIRECTION = { 1: new cx(0, -1), }; -const move = (prev, curr, c) => cx.add(prev, cx.mult(c, cx.sub(curr, prev))); - const construct_moves = (curr, prev) => Object.keys(DIRECTION).map((x) => move(curr, prev, DIRECTION[x])); -- 2.45.2 From 078a5c38b480b04073281f8c7a94445afaf6f50a Mon Sep 17 00:00:00 2001 From: Lizzy Hunt Date: Fri, 24 Feb 2023 12:15:06 -0700 Subject: [PATCH 4/5] Add pred path --- euler-golf/js/game.js | 20 +++++++++++++++++--- euler-golf/js/json-ds.js | 2 ++ euler-golf/js/sol.js | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/euler-golf/js/game.js b/euler-golf/js/game.js index 4edaa37..0f76f17 100644 --- a/euler-golf/js/game.js +++ b/euler-golf/js/game.js @@ -2,7 +2,7 @@ const DEFAULTS = { max_rows: 80, max_cols: 80, min_gap: 40, - angle_multiplier: 0.0005, + angle_multiplier: 10e-4, }; const CANVAS = document.getElementById("canvas"); @@ -137,7 +137,7 @@ const handle_input = (state, dt) => { const render = ({ width, height, ctx, rows, cols } = state) => { ctx.clearRect(0, 0, width, height); - ctx.fillStyle = "rgba(255, 255, 255, 0)"; + ctx.fillStyle = "rgba(0, 0, 0, 0)"; ctx.fillRect(0, 0, width, height); const grid_spec = calculate_grid_spec(state); @@ -156,11 +156,25 @@ const render = ({ width, height, ctx, rows, cols } = state) => { complex_to_grid(cx.add(new cx(angle_re, angle_im), prev), rows, cols), ]); + ctx.line( + grid_to_canvas(complex_to_grid(curr, rows, cols), grid_spec), + grid_to_canvas( + complex_to_grid( + move(prev, curr, new cx(0, state.angle.im < 0 ? -1 : 1)), + rows, + cols + ), + grid_spec + ), + 6, + "#aaa" + ); + ctx.cartesian_grid(rows, cols, grid_spec, (x, y) => { if (x == Math.floor(cols / 2) && y == Math.floor(rows / 2)) { return { radius: 7, - color: "#0000ff", + color: "#2f9c94", }; } else { return { diff --git a/euler-golf/js/json-ds.js b/euler-golf/js/json-ds.js index 24a8729..dc7e88e 100644 --- a/euler-golf/js/json-ds.js +++ b/euler-golf/js/json-ds.js @@ -1,5 +1,6 @@ class JSONSet { items = new Set(); + constructor(initial) { if (Array.isArray(initial)) { initial.map((x) => this.apply_set_function("add", x)); @@ -11,6 +12,7 @@ class JSONSet { (f_name) => (this[f_name] = (x) => this.apply_set_function(f_name, x)) ); } + apply_set_function(f_name, x) { return this.items[f_name](JSON.stringify(x)); } diff --git a/euler-golf/js/sol.js b/euler-golf/js/sol.js index be4a9aa..5747178 100644 --- a/euler-golf/js/sol.js +++ b/euler-golf/js/sol.js @@ -1,4 +1,4 @@ -const DEPTH = 21; +const DEPTH = 22; const DIRECTION = { 0: new cx(0, 1), -- 2.45.2 From 8050a399dfdf92692ffe2bec1b980b8988551fa0 Mon Sep 17 00:00:00 2001 From: Simponic Date: Fri, 24 Feb 2023 15:00:30 -0700 Subject: [PATCH 5/5] Finish euler golf --- euler-golf/css/styles.css | 36 +++++++++- euler-golf/index.html | 59 ++++++++++++++- euler-golf/js/controls.js | 32 +++++++++ euler-golf/js/game.js | 111 ++++++++++++++++++++--------- euler-golf/js/modal-vanilla.min.js | 1 + euler-golf/js/sol.js | 4 +- index.html | 14 ++++ 7 files changed, 215 insertions(+), 42 deletions(-) create mode 100644 euler-golf/js/controls.js create mode 100644 euler-golf/js/modal-vanilla.min.js diff --git a/euler-golf/css/styles.css b/euler-golf/css/styles.css index 05f2f9f..e9885c5 100644 --- a/euler-golf/css/styles.css +++ b/euler-golf/css/styles.css @@ -24,21 +24,51 @@ body { height: 100vw; } -.button { - border: 1px solid black; +button { border-radius: 5px; padding: 5px; cursor: pointer; + margin-left: 5px; } .controls { + cursor: pointer; padding: 12px; position: fixed; bottom: 0; right: 0; - background-color: rgba(255, 255, 255, 0.5); + background-color: rgba(255, 255, 255, 0.8); border: 1px solid white; border-radius: 8px; margin-right: 6px; margin-bottom: 6px; } + +.buttons { + display: flex; + justify-content: space-around; + align-items: center; +} + +.modal { + display: flex; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + + width: 80vw; + max-width: 500px; + min-height: 200px; + + padding: 12px; + + background-color: rgba(255, 255, 255, 0.8); + border: 1px solid black; + border-radius: 15px; +} + +.modal-body { + display: flex; + justify-content: center; +} diff --git a/euler-golf/index.html b/euler-golf/index.html index fb31d41..7679a34 100644 --- a/euler-golf/index.html +++ b/euler-golf/index.html @@ -7,13 +7,68 @@ -
- +
+ + โ†‘โ†‘
+ + + + + diff --git a/euler-golf/js/controls.js b/euler-golf/js/controls.js new file mode 100644 index 0000000..d89c53d --- /dev/null +++ b/euler-golf/js/controls.js @@ -0,0 +1,32 @@ +const directions_modal = new Modal({ + el: document.getElementById("directions-modal"), +}); + +document + .getElementById("controls-container") + .addEventListener("mouseover", () => { + document.getElementById("controls").style.display = "block"; + document.getElementById("expand-show").style.display = "none"; + }); +document + .getElementById("controls-container") + .addEventListener("mouseout", () => { + document.getElementById("controls").style.display = "none"; + document.getElementById("expand-show").style.display = "inline"; + }); + +document.getElementById("reset").addEventListener("click", () => { + state = reset_state(state); + + state.target = rand_target(state.rows, state.cols); +}); + +document.getElementById("solve").addEventListener("click", () => { + if (!cx.eq(state.path.at(-2), new cx(0, 0))) state = reset_state(state); + + state.solution = sol(state.target); +}); + +document + .getElementById("directions") + .addEventListener("click", () => directions_modal.show()); diff --git a/euler-golf/js/game.js b/euler-golf/js/game.js index 0f76f17..68fca94 100644 --- a/euler-golf/js/game.js +++ b/euler-golf/js/game.js @@ -1,18 +1,16 @@ const DEFAULTS = { max_rows: 80, max_cols: 80, - min_gap: 40, + min_gap: 30, angle_multiplier: 10e-4, }; const CANVAS = document.getElementById("canvas"); let state = { - grid_padding: 10, + grid_padding: 30, canvas: CANVAS, ctx: CANVAS.getContext("2d"), last_render: 0, - path: [new cx(0, 0), new cx(1, 0)], - angle: new cx(0, 0), keys: {}, changes: {}, }; @@ -59,7 +57,7 @@ CanvasRenderingContext2D.prototype.draw_cartesian_path = function ( CanvasRenderingContext2D.prototype.do_grid = function ( rows, cols, - draw_at_grid_pos = (ctx, x, y) => ctx.circle(x, y, 10, "#00ff00") + draw_at_grid_pos = (ctx, x, y) => ctx.circle(x, y, 10, "#44ff44") ) { for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { @@ -88,9 +86,11 @@ const move = (prev, curr, c) => cx.add(prev, cx.mult(c, cx.sub(curr, prev))); const rand_between = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; -const rand_target = (cols, rows) => { - const res = new cx(rand_between(0, cols), rand_between(0, rows)); - if (res.re % 2 || res.im % 2) return rand_target(cols, rows); +const rand_target = (rows, cols) => { + const r = Math.floor(rows / 2); + const c = Math.floor(cols / 2); + const res = new cx(rand_between(-c, c), rand_between(-r, r)); + if (!sol(res)) return rand_target(rows, cols); return res; }; @@ -121,21 +121,26 @@ const complex_to_grid = (c, rows, cols) => { }; // Game loop + +const maybe_add_state_angle_move = ({ angle } = state) => { + if (angle.im <= -1 || angle.im >= 1) { + angle.im = angle.im <= -1 ? -1 : 1; + state.path.push(move(state.path.at(-2), state.path.at(-1), angle)); + state.angle = new cx(0, 0); + } + return state; +}; + const handle_input = (state, dt) => { if (state.keys.ArrowLeft) { state.angle.im += DEFAULTS.angle_multiplier * dt; } else if (state.keys.ArrowRight) { state.angle.im -= DEFAULTS.angle_multiplier * dt; } - - if (state.angle.im <= -1 || state.angle.im >= 1) { - state.angle.im = state.angle.im <= -1 ? -1 : 1; - state.path.push(move(state.path.at(-2), state.path.at(-1), state.angle)); - state.angle = new cx(0, 0); - } + state = maybe_add_state_angle_move(state); }; -const render = ({ width, height, ctx, rows, cols } = state) => { +const render = ({ width, height, ctx, rows, cols, target } = state) => { ctx.clearRect(0, 0, width, height); ctx.fillStyle = "rgba(0, 0, 0, 0)"; ctx.fillRect(0, 0, width, height); @@ -156,19 +161,17 @@ const render = ({ width, height, ctx, rows, cols } = state) => { complex_to_grid(cx.add(new cx(angle_re, angle_im), prev), rows, cols), ]); - ctx.line( - grid_to_canvas(complex_to_grid(curr, rows, cols), grid_spec), - grid_to_canvas( - complex_to_grid( - move(prev, curr, new cx(0, state.angle.im < 0 ? -1 : 1)), - rows, - cols - ), - grid_spec - ), - 6, - "#aaa" - ); + if (!(state.angle.im == state.angle.re && state.angle.re == 0)) { + // Draw path to next player's target + const [a, b] = [ + curr, + move(prev, curr, new cx(0, state.angle.im < 0 ? -1 : 1)), + ].map((c) => grid_to_canvas(complex_to_grid(c, rows, cols), grid_spec)); + + ctx.line(a, b, 6, "#aaa"); + } + + const grid_target = complex_to_grid(target, rows, cols); ctx.cartesian_grid(rows, cols, grid_spec, (x, y) => { if (x == Math.floor(cols / 2) && y == Math.floor(rows / 2)) { @@ -176,6 +179,11 @@ const render = ({ width, height, ctx, rows, cols } = state) => { radius: 7, color: "#2f9c94", }; + } else if (x == grid_target.x && y == grid_target.y) { + return { + radius: 8, + color: "#00ff00", + }; } else { return { radius: 3, @@ -191,25 +199,48 @@ const loop = (now) => { if (Object.keys(state.changes).length > 0) { if (state.changes.width || state.changes.height) { - state.changes.rows = Math.min( - DEFAULTS.max_rows, - state.changes.height / DEFAULTS.min_gap + state.changes.rows = Math.floor( + Math.min(DEFAULTS.max_rows, state.changes.height / DEFAULTS.min_gap) ); - state.changes.cols = Math.min( - DEFAULTS.max_cols, - state.changes.width / DEFAULTS.min_gap + state.changes.cols = Math.floor( + Math.min(DEFAULTS.max_cols, state.changes.width / DEFAULTS.min_gap) ); } state = { ...state, ...state.changes }; + state.changes = {}; } - handle_input(state, dt); + if (!state.target) { + state.target = rand_target(state.rows, state.cols); + } + + if (!state.solution) { + handle_input(state, dt); + } else { + if (!state?.solution.length) { + delete state.solution; + } else { + state.angle.im += + (state.solution[0] === "-" ? 1 : -1) * DEFAULTS.angle_multiplier * dt; + + state = maybe_add_state_angle_move(state); + + if (cx.eq(state.angle, new cx(0, 0))) state.solution.shift(); + } + } render(state); requestAnimationFrame(loop); }; +const reset_state = ({ rows, cols } = state) => ({ + ...state, + solution: null, + path: [new cx(0, 0), new cx(1, 0)], + angle: new cx(0, 0), +}); + // DOM const on_resize = () => { CANVAS.width = document.body.clientWidth; @@ -232,4 +263,14 @@ window.addEventListener("keyup", on_keyup); // main on_resize(); +state = reset_state(state); + +if (!sessionStorage.getItem("seen-instructions")) { + new Modal({ + el: document.getElementById("directions-modal"), + }).show(); + + sessionStorage.setItem("seen-instructions", true); +} + requestAnimationFrame(loop); diff --git a/euler-golf/js/modal-vanilla.min.js b/euler-golf/js/modal-vanilla.min.js new file mode 100644 index 0000000..0d314c7 --- /dev/null +++ b/euler-golf/js/modal-vanilla.min.js @@ -0,0 +1 @@ +var Modal=function(e){function t(i){if(n[i])return n[i].exports;var o=n[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,i){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:i})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=0)}([function(e,t,n){e.exports=n(1).default},function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function r(e){for(var t in e)Array.isArray(e[t])?e[t].forEach(function(e){r(e)}):null!==e[t]&&"object"===p(e[t])&&Object.freeze(e[t]);return Object.freeze(e)}function a(){return(65536*(1+Math.random())|0).toString(16)+(65536*(1+Math.random())|0).toString(16)}function l(e,t,n){var i=e.data||{};if(void 0===n){if(e.data&&e.data[t])return e.data[t];var o=e.getAttribute("data-"+t);return void 0!==o?o:null}return i[t]=n,e.data=i,e}function d(e,t){return e.nodeName?e:(e=e.replace(/(\t|\n$)/g,""),_||(_=document.createElement("div")),_.innerHTML="",_.innerHTML=e,!0===t?_.childNodes:_.childNodes[0])}function c(){var e=void 0,t=void 0,n=void 0,i=document.createElement("div");return v(i.style,{visibility:"hidden",width:"100px"}),document.body.appendChild(i),n=i.offsetWidth,i.style.overflow="scroll",e=document.createElement("div"),e.style.width="100%",i.appendChild(e),t=n-e.offsetWidth,document.body.removeChild(i),t}function h(e){for(var t=[e];e.parentNode;)e=e.parentNode,t.push(e);return t}Object.defineProperty(t,"__esModule",{value:!0});var u=function(){function e(e,t){for(var n=0;n
',dialog:'',content:'',header:'',headerClose:'',body:'',footer:'',backdrop:''},k=function(e){function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};i(this,t);var n=o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this));if(n.id=a(),n.el=null,n._html={},n._events={},n._visible=!1,n._pointerInContent=!1,n._options=v({},t.options,e),n._templates=v({},t.templates,e.templates||{}),n._html.appendTo=document.querySelector(n._options.appendTo),n._scrollbarWidth=c(),null===n._options.buttons&&(n._options.buttons=t.buttons.dialog),n._options.el){var s=n._options.el;if("string"==typeof n._options.el&&!(s=document.querySelector(n._options.el)))throw new Error("Selector: DOM Element "+n._options.el+" not found.");l(s,"modal",n),n.el=s}else n._options.construct=!0;return n._options.construct?n._render():n._mapDom(),n}return s(t,e),u(t,null,[{key:"alert",value:function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return new t(v({},y,{title:e,content:!1,construct:!0,headerClose:!1,buttons:t.buttons.alert},n))}},{key:"confirm",value:function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return new t(v({},y,{title:e,content:!1,construct:!0,headerClose:!1,buttons:t.buttons.confirm},n))}},{key:"templates",set:function(e){this._baseTemplates=e},get:function(){return v({},g,t._baseTemplates||{})}},{key:"buttons",set:function(e){this._baseButtons=e},get:function(){return v({},b,t._baseButtons||{})}},{key:"options",set:function(e){this._baseOptions=e},get:function(){return v({},y,t._baseOptions||{})}},{key:"version",get:function(){return"0.12.0"}}]),u(t,[{key:"_render",value:function(){var e=this._html,t=this._options,n=this._templates,i=!!t.animate&&t.animateClass;return e.container=d(n.container),e.dialog=d(n.dialog),e.content=d(n.content),e.header=d(n.header),e.headerClose=d(n.headerClose),e.body=d(n.body),e.footer=d(n.footer),i&&e.container.classList.add(i),this._setHeader(),this._setContent(),this._setFooter(),this.el=e.container,e.dialog.appendChild(e.content),e.container.appendChild(e.dialog),this}},{key:"_mapDom",value:function(){var e=this._html,t=this._options;return this.el.classList.contains(t.animateClass)&&(t.animate=!0),e.container=this.el,e.dialog=this.el.querySelector(".modal-dialog"),e.content=this.el.querySelector(".modal-content"),e.header=this.el.querySelector(".modal-header"),e.headerClose=this.el.querySelector(".modal-header .close"),e.body=this.el.querySelector(".modal-body"),e.footer=this.el.querySelector(".modal-footer"),this._setHeader(),this._setContent(),this._setFooter(),this}},{key:"_setHeader",value:function(){var e=this._html,t=this._options;t.header&&e.header&&(t.title.nodeName?e.header.innerHTML=t.title.outerHTML:"string"==typeof t.title&&(e.header.innerHTML='"),null===this.el&&e.headerClose&&t.headerClose&&e.header.appendChild(e.headerClose),t.construct&&e.content.appendChild(e.header))}},{key:"_setContent",value:function(){var e=this._html,t=this._options;t.content&&e.body&&("string"==typeof t.content?e.body.innerHTML=t.content:e.body.innerHTML=t.content.outerHTML,t.construct&&e.content.appendChild(e.body))}},{key:"_setFooter",value:function(){var e=this._html,t=this._options;t.footer&&e.footer&&(t.footer.nodeName?e.footer.ineerHTML=t.footer.outerHTML:"string"==typeof t.footer?e.footer.innerHTML=t.footer:e.footer.children.length||t.buttons.forEach(function(t){var n=document.createElement("button");l(n,"button",t),n.innerHTML=t.text,n.setAttribute("type","button");for(var i in t.attr)n.setAttribute(i,t.attr[i]);e.footer.appendChild(n)}),t.construct&&e.content.appendChild(e.footer))}},{key:"_setEvents",value:function(){var e=(this._options,this._html);this._events.keydownHandler=this._handleKeydownEvent.bind(this),document.body.addEventListener("keydown",this._events.keydownHandler),this._events.mousedownHandler=this._handleMousedownEvent.bind(this),e.container.addEventListener("mousedown",this._events.mousedownHandler),this._events.clickHandler=this._handleClickEvent.bind(this),e.container.addEventListener("click",this._events.clickHandler),this._events.resizeHandler=this._handleResizeEvent.bind(this),window.addEventListener("resize",this._events.resizeHandler)}},{key:"_handleMousedownEvent",value:function(e){var t=this;this._pointerInContent=!1,h(e.target).every(function(e){return!e.classList||!e.classList.contains("modal-content")||(t._pointerInContent=!0,!1)})}},{key:"_handleClickEvent",value:function(e){var t=this;h(e.target).every(function(n){return!("HTML"===n.tagName||!0!==t._options.backdrop&&n.classList.contains("modal")||n.classList.contains("modal-content")||("modal"===n.getAttribute("data-dismiss")?(t.emit("dismiss",t,e,l(e.target,"button")),t.hide(),1):!t._pointerInContent&&n.classList.contains("modal")&&(t.emit("dismiss",t,e,null),t.hide(),1)))}),this._pointerInContent=!1}},{key:"_handleKeydownEvent",value:function(e){27===e.which&&this._options.keyboard&&(this.emit("dismiss",this,e,null),this.hide())}},{key:"_handleResizeEvent",value:function(e){this._resize()}},{key:"show",value:function(){var e=this,t=this._options,n=this._html;return this.emit("show",this),this._checkScrollbar(),this._setScrollbar(),document.body.classList.add("modal-open"),t.construct&&n.appendTo.appendChild(n.container),n.container.style.display="block",n.container.scrollTop=0,!1!==t.backdrop?(this.once("showBackdrop",function(){e._setEvents(),t.animate&&n.container.offsetWidth,n.container.classList.add(t.animateInClass),setTimeout(function(){e._visible=!0,e.emit("shown",e)},t.transition)}),this._backdrop()):(this._setEvents(),t.animate&&n.container.offsetWidth,n.container.classList.add(t.animateInClass),setTimeout(function(){e._visible=!0,e.emit("shown",e)},t.transition)),this._resize(),this}},{key:"toggle",value:function(){this._visible?this.hide():this.show()}},{key:"_resize",value:function(){var e=this._html.container.scrollHeight>document.documentElement.clientHeight;this._html.container.style.paddingLeft=!this.bodyIsOverflowing&&e?this._scrollbarWidth+"px":"",this._html.container.style.paddingRight=this.bodyIsOverflowing&&!e?this._scrollbarWidth+"px":""}},{key:"_backdrop",value:function(){var e=this,t=this._html,n=this._templates,i=this._options,o=!!i.animate&&i.animateClass;t.backdrop=d(n.backdrop),o&&t.backdrop.classList.add(o),t.appendTo.appendChild(t.backdrop),o&&t.backdrop.offsetWidth,t.backdrop.classList.add(i.animateInClass),setTimeout(function(){e.emit("showBackdrop",e)},this._options.backdropTransition)}},{key:"hide",value:function(){var e=this,t=this._html,n=this._options,i=t.container.classList;if(this.emit("hide",this),i.remove(n.animateInClass),n.backdrop){t.backdrop.classList.remove(n.animateInClass)}return this._removeEvents(),setTimeout(function(){document.body.classList.remove("modal-open"),document.body.style.paddingRight=e.originalBodyPad},n.backdropTransition),setTimeout(function(){n.backdrop&&t.backdrop.parentNode.removeChild(t.backdrop),t.container.style.display="none",n.construct&&t.container.parentNode.removeChild(t.container),e._visible=!1,e.emit("hidden",e)},n.transition),this}},{key:"_removeEvents",value:function(){this._events.keydownHandler&&document.body.removeEventListener("keydown",this._events.keydownHandler),this._html.container.removeEventListener("mousedown",this._events.mousedownHandler),this._html.container.removeEventListener("click",this._events.clickHandler),window.removeEventListener("resize",this._events.resizeHandler)}},{key:"_checkScrollbar",value:function(){this.bodyIsOverflowing=document.body.clientWidth0&&this._events[e].length>o&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace()),this},n.prototype.on=n.prototype.addListener,n.prototype.once=function(e,t){function n(){this.removeListener(e,n),o||(o=!0,t.apply(this,arguments))}if(!i(t))throw TypeError("listener must be a function");var o=!1;return n.listener=t,this.on(e,n),this},n.prototype.removeListener=function(e,t){var n,o,r,a;if(!i(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(n=this._events[e],r=n.length,o=-1,n===t||i(n.listener)&&n.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(s(n)){for(a=r;a-- >0;)if(n[a]===t||n[a].listener&&n[a].listener===t){o=a;break}if(o<0)return this;1===n.length?(n.length=0,delete this._events[e]):n.splice(o,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},n.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(n=this._events[e],i(n))this.removeListener(e,n);else if(n)for(;n.length;)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},n.prototype.listeners=function(e){return this._events&&this._events[e]?i(this._events[e])?[this._events[e]]:this._events[e].slice():[]},n.prototype.listenerCount=function(e){if(this._events){var t=this._events[e];if(i(t))return 1;if(t)return t.length}return 0},n.listenerCount=function(e,t){return e.listenerCount(t)}}]); diff --git a/euler-golf/js/sol.js b/euler-golf/js/sol.js index 5747178..bc403fc 100644 --- a/euler-golf/js/sol.js +++ b/euler-golf/js/sol.js @@ -1,4 +1,4 @@ -const DEPTH = 22; +const DEPTH = 15; const DIRECTION = { 0: new cx(0, 1), @@ -13,7 +13,7 @@ const backtrack = (local_index, depth) => .toString(2) .padStart(depth, "0") .split("") - .map((direction) => (Number(direction) ? "-" : "+")); + .map((direction) => (Number(direction) ? "+" : "-")); const sol = (target, start_from = new cx(0, 0), start_to = new cx(1, 0)) => { let moves = [start_to, ...construct_moves(start_from, start_to)]; diff --git a/index.html b/index.html index 758b4e0..0381a91 100644 --- a/index.html +++ b/index.html @@ -27,6 +27,20 @@ simponic.xyz.
+
+
+ +
+ +
+

Euler Golf 2

+

+ A puzzle game (with solver) to explore rotations in the complex + plane. +

+
+
+
-- 2.45.2