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;
};