Euler golf #1
24
euler-golf/css/styles.css
Normal file
24
euler-golf/css/styles.css
Normal file
@ -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;
|
||||||
|
}
|
@ -2,36 +2,15 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Euler Golf 2</title>
|
<title>Euler Golf 2</title>
|
||||||
<style>
|
<link rel="stylesheet" type="text/css" href="css/styles.css" />
|
||||||
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%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
border: 1px solid black;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<canvas id="canvas"></canvas>
|
<div class="canvas-holder">
|
||||||
|
<canvas id="canvas" width="1000" height="1000"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="js/cx.js"></script>
|
<script src="js/cx.js"></script>
|
||||||
|
<script src="js/json-ds.js"></script>
|
||||||
<script src="js/sol.js"></script>
|
<script src="js/sol.js"></script>
|
||||||
<script src="js/game.js"></script>
|
<script src="js/game.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -1,11 +1,123 @@
|
|||||||
const WIDTH = 1000;
|
const CANVAS_ID = "myCanvas";
|
||||||
const HEIGHT = 1000;
|
const DEFAULTS = {
|
||||||
|
rows: 15,
|
||||||
const draw_grid = (ctx) => {};
|
cols: 15,
|
||||||
|
grid_padding: 10,
|
||||||
const main = () => {
|
|
||||||
const canvas = document.getElementById("canvas");
|
|
||||||
const ctx = canvas.getContext("2d");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
|
17
euler-golf/js/json-ds.js
Normal file
17
euler-golf/js/json-ds.js
Normal file
@ -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));
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
const DEPTH = 7;
|
const DEPTH = 21;
|
||||||
|
|
||||||
const DIRECTION = {
|
const DIRECTION = {
|
||||||
0: new cx(0, 1),
|
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 move = (prev, curr, c) => cx.add(prev, cx.mult(c, cx.sub(curr, prev)));
|
||||||
|
|
||||||
const construct_moves = (curr, prev) =>
|
const construct_moves = (curr, prev) =>
|
||||||
Object.keys(DIRECTION).map((x) => move(curr, prev, DIRECTION[x]));
|
Object.keys(DIRECTION).map((x) => move(curr, prev, DIRECTION[x]));
|
||||||
|
|
||||||
const backtrack = (local_index, depth) =>
|
const backtrack = (local_index, depth) =>
|
||||||
local_index
|
local_index
|
||||||
.toString(2)
|
.toString(2)
|
||||||
.padStart(depth, "0")
|
.padStart(depth, "0")
|
||||||
.split("")
|
.split("")
|
||||||
.reverse()
|
.map((direction) => (Number(direction) ? "-" : "+"));
|
||||||
.map((dir) => (dir ? "+" : "-"));
|
|
||||||
|
|
||||||
const sol = (target, start_from = new cx(0, 0), start_to = new cx(1, 0)) => {
|
const sol = (target, start_from = new cx(0, 0), start_to = new cx(1, 0)) => {
|
||||||
let moves = construct_moves(start_from, start_to);
|
let moves = [start_to, ...construct_moves(start_from, start_to)];
|
||||||
let curr_depth = 2; // potential bug: when target is within one move away
|
let curr_depth = 2;
|
||||||
|
|
||||||
do {
|
while (curr_depth < DEPTH) {
|
||||||
for (let i = 0; i < Math.pow(2, curr_depth); i++) {
|
for (let i = 0; i < Math.pow(2, curr_depth); i++) {
|
||||||
const current_i =
|
const direction = DIRECTION[Number(i.toString(2).at(-1))];
|
||||||
(i >> 1) + ((1 - Math.pow(2, curr_depth - 1)) / (1 - 2) - 1);
|
// Current element is at i >> 1 + the offset for the previous group (which is
|
||||||
const previous_i =
|
// the sum of the geometric series 2**n until curr_depth - 1)
|
||||||
(i >> 2) + ((1 - Math.pow(2, curr_depth - 2)) / (1 - 2) - 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(
|
const new_move = move(moves[previous_i], moves[current_i], direction);
|
||||||
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);
|
moves.push(new_move);
|
||||||
|
if (cx.eq(new_move, target)) return backtrack(i, curr_depth);
|
||||||
}
|
}
|
||||||
curr_depth++;
|
curr_depth++;
|
||||||
} while (curr_depth < DEPTH);
|
}
|
||||||
console.log(moves);
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user