Skip to content

Commit

Permalink
move to arrs
Browse files Browse the repository at this point in the history
  • Loading branch information
russellsamora committed Jan 14, 2025
1 parent db60552 commit c72ef32
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 28 deletions.
43 changes: 26 additions & 17 deletions src/components/Game.svelte
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<script>
import Grid from "$components/Grid.svelte";
import Keypad from "$components/Keypad.svelte";
import { mode } from "$runes/misc.svelte.js";
import { game } from "$runes/misc.svelte.js";
const size = 10;
const targetCount = 100;
let position = $state({ x: 0, y: 0 });
let path = $state([{ x: 0, y: 0 }]);
let position = $state([0, 0]);
let path = $state([[0, 0]]);
let visited = $state({});
let visitedCount = $derived(Object.keys(visited).length + 1);
let complete = $derived(visitedCount === targetCount);
function reveal() {
mode.game = false;
game.active = false;
if (complete)
document
.querySelectorAll("span.you")
Expand All @@ -25,26 +25,35 @@
document.getElementById("results").classList.add("visible");
}
function submit() {
const str = path.map((p) => p.join(",")).join("|");
console.log(str);
}
function onmove(key) {
let dir;
if (key === "ArrowUp") dir = { x: 0, y: -1 };
else if (key === "ArrowDown") dir = { x: 0, y: 1 };
else if (key === "ArrowLeft") dir = { x: -1, y: 0 };
else if (key === "ArrowRight") dir = { x: 1, y: 0 };
if (key === "ArrowUp") dir = [0, -1];
else if (key === "ArrowDown") dir = [0, 1];
else if (key === "ArrowLeft") dir = [-1, 0];
else if (key === "ArrowRight") dir = [1, 0];
position.x += dir.x;
position.y += dir.y;
position[0] += dir[0];
position[1] += dir[1];
position.x = Math.max(0, Math.min(size - 1, position.x));
position.y = Math.max(0, Math.min(size - 1, position.y));
path.push({ x: position.x, y: position.y });
visited[position.x + "," + position.y] = true;
// get length of visited
position[0] = Math.max(0, Math.min(size - 1, position[0]));
position[1] = Math.max(0, Math.min(size - 1, position[1]));
path.push([...position]);
visited[position.join(",")] = true;
// TODO get length of visited?
}
$effect(() => {
if (complete) reveal();
if (complete) {
game.completed = true;
submit();
reveal();
}
});
</script>

Expand All @@ -55,7 +64,7 @@
</p>

<Grid {size} {path} perspective={true}></Grid>
<Keypad {onmove} {mode}></Keypad>
<Keypad {onmove} active={game.active}></Keypad>

<style>
.skip {
Expand Down
14 changes: 7 additions & 7 deletions src/components/Grid.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
const defaultCells = Array(size ** 2)
.fill()
.map((_, i) => ({
x: i % size,
y: Math.floor(i / size),
pos: [i % size, Math.floor(i / size)],
obstacle: false,
visited: false
}));
Expand All @@ -15,8 +14,7 @@
const all = defaultCells.map((c) => ({ ...c }));
path.forEach((p, i) => {
// todo should we just be smart and use arrays?
const c = all.find((c) => c.x === p.x && c.y === p.y);
const c = all.find((c) => c.pos[0] === p[0] && c.pos[1] === p[1]);
c.visited = true;
});
return all;
Expand All @@ -43,8 +41,10 @@
bind:offsetWidth
>
<div class="grid">
{#each cells as { obstacle, visited, x, y }}
{@const active = x === latest.x && y === latest.y}
{#each cells as { obstacle, visited, pos }}
{@const x = pos[0]}
{@const y = pos[1]}
{@const active = x === latest[0] && y === latest[1]}
<div
class="cell"
class:obstacle
Expand All @@ -59,7 +59,7 @@
{/each}
</div>
<div class="grid mower">
<div class="cube" style="--x: {latest.x}; --y: {latest.y};">
<div class="cube" style="--x: {latest[0]}; --y: {latest[1]};">
<div class="face top"></div>
<div class="face front"></div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/components/Keypad.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<script>
import { MediaQuery } from "svelte/reactivity";
let { onmove, mode } = $props();
let { onmove, active } = $props();
const hover = new MediaQuery("hover: hover");
const pointer = new MediaQuery("pointer: fine");
let desktop = $derived(hover && pointer);
function onKeydown(e) {
if (!mode.game) return;
if (!active) return;
const keys = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"];
if (!keys.includes(e.key)) return;
onmove(e.key);
Expand Down
2 changes: 1 addition & 1 deletion src/data/copy.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"title":"Mow this lawn. Learn about your brain.","description":"Your lawn mowing style says more about you than you think.","byline":"By <a href=https://pudding.cool/author/russell-samora target=_blank rel=noreferrer>Russell Samora</a><br>&amp; <a href=https://pudding.cool/author/ilia-blinderman target=_blank rel=noreferrer>Ilia Blinderman</a>","body":[{"section":"story","content":[{"type":"text","value":"This is a story about how our minds work. But first, let’s start with a game: <strong>how efficiently can you mow this lawn?</strong>"}]},{"section":"Game"},{"section":"results","content":[{"type":"text","value":"So far, <strong class=field>X</strong> other people have mowed this lawn. <span class=you>Your technique was better than <strong class=percent-vs-field>X%</strong> of them. <strong class=custom-field></strong></span>"},{"type":"text","value":"<span class=you>But…you were <strong class=percent-vs-optimal>X%</strong> less efficient than the optimal path.</span> If we abstract the mowing, it’s really just a path formed by connecting nodes. The optimal path looks like this:"},{"type":"Result","value":{}},{"type":"text","value":"<span class=custom-humans>Surprisingly,</span> most humans are actually really good at finding solutions to pathfinding problems like this. The most popular example of pathfinding is the <a href=https://en.wikipedia.org/wiki/Travelling_salesman_problem target=_blank rel=noreferrer>Traveling Salesman Problem.</a>"},{"type":"Video","value":{"src":"assets/videos/tsp.mp4","preload":"true","autoplay":"true","muted":"true","loop":"true","figcaption":"From Daniel Shiffman’s <a href=https://www.youtube.com/watch?v=BAejnwN4Ccw target=_blank rel=noreferrer>Coding Rainbow</a>"}},{"type":"text","value":"The premise is simple: a salesperson must visit a set of cities exactly once and return to their starting point, all while taking the shortest possible route. We see this all the time in the real world. From a pizza delivery driver planning their stops to a trick-or-treater maximizing their candy haul, finding the most efficient routes would be solving a version of this problem."},{"type":"text","value":"I <a href=https://docs.lib.purdue.edu/jps/vol1/iss1/4/ target=_blank rel=noreferrer>read a study</a> that found humans came impressively close to computer-calculated optimal solutions to this problem with fewer stops, achieving solutions just 11% less efficient than optimal for routes with 70+ stops."},{"type":"Img","value":{"src":"assets/images/study.png","alt":"A chart depicting how people perform on the Traveling Salesman Problem compared to the optimal path, which shows that people perform near-ideal at fewer nodes, with a slow increasing deviation as more nodes are added","figcaption":"Chart of findings from <a href=https://docs.lib.purdue.edu/jps/vol1/iss1/4/ target=_blank rel=noreferrer>this study</a>"}},{"type":"text","value":"<strong>I was curious; how well might humans perform on a similar optimization problem?</strong>"},{"type":"text","value":"But first, a <em>very quick</em> computer science primer. There are two main ways to tackle problems like these: <strong>algorithms</strong> that calculate the optimal path and <strong>heuristics</strong>—simple strategies for completing the task."},{"type":"text","value":"<strong>Algorithms</strong> solve the problem with an estimated optimal solution. But that can come at a cost as complexity grows. For example, brute force calculations for a salesperson visiting <strong>just 10 cities</strong> requires testing <strong>3.6 million</strong> possible routes, and that number explodes exponentially as more cities are added."},{"type":"text","value":"<strong>15 cities</strong> and we’re at <strong>1.3 trillion</strong> possible routes. 😱"},{"type":"text","value":"<strong>20 cities</strong> balloons to <strong>2.4 quintillion</strong> possible routes. 🤯"},{"type":"text","value":"<strong>Heuristics</strong> are a lot like human problem-solving; they aim for a good-enough strategy that solves the problem quickly. For example, choosing the closest city at each step isn’t perfect, but it’s much faster and usually close to the optimal route."},{"type":"text","value":"For our experiment, we’ll stick with heuristics."},{"type":"text","value":"<span class=you><strong>What type of computer-y brain do you have?</strong> Our sorting hat determined that your mowing style is most similar to the <strong class=sorting-hat>X</strong> heuristic.</span><span class=skip>So what type of computer-brain do most people have?</span>"},{"type":"text","value":"<mark>(graphic breakdown of all users and the most similar heuristic types)</mark>"},{"type":"text","value":"We chose to look at seven different heuristics for our lawn mower game, which is a problem more formally called <strong>Coverage Path Planning.</strong> To focus on your path planning vs. driving skills, we simplified the world into a grid of cells."},{"type":"text","value":"Each heuristic follows a different approach to visit each cell on the grid. To get a sense of their general style, here are all seven heuristics on an empty grid with no obstacles."},{"type":"text","value":"<mark>(graphic animate all 7, rate efficiency)</mark>"},{"type":"text","value":"But an open lawn or an empty grid isn’t that interesting. Things get more fun—and complex—when we introduce obstacles."},{"type":"text","value":"<mark>(graphic animate all 7, rate efficiency)</mark>"},{"type":"text","value":"But this is just one layout. We gave each heuristic 100 different layouts to see how they handled various situations."},{"type":"text","value":"<mark>(graphic some other viz, maybe high level results)</mark>"},{"type":"text","value":"Tk talk about complexity scores and Big-O notation. Why not."},{"type":"text","value":"Okay so that is the heuristics. Let’s get back to the humans. Here is a distribution of how well people did compared to the optimal solution."},{"type":"text","value":"<mark>(graphic breakdown of how optimal everyone performed / you)</mark>"},{"type":"text","value":"Tk explanation of why humans are good at this."},{"type":"text","value":"Tk wrap things up."},{"type":"text","value":"<mark>(graphic maybe a situation changer so they can go up against all 100 layouts from earlier)</mark>"}]}]}
{"title":"Mow this lawn. Learn about your brain.","description":"Your lawn mowing style says more about you than you think.","byline":"By <a href=https://pudding.cool/author/russell-samora target=_blank rel=noreferrer>Russell Samora</a><br>&amp; <a href=https://pudding.cool/author/ilia-blinderman target=_blank rel=noreferrer>Ilia Blinderman</a>","body":[{"section":"story","content":[{"type":"text","value":"This is a story about how our minds work. But first, let’s start with a game: <strong>how efficiently can you mow this lawn?</strong>"}]},{"section":"Game"},{"section":"results","content":[{"type":"text","value":"So far, <strong class=field>X</strong> other people have mowed this lawn. <span class=you>Your technique was better than <strong class=percent-vs-field>X%</strong> of them. <strong class=custom-field></strong></span>"},{"type":"text","value":"<span class=you>But…you were <strong class=percent-vs-optimal>X%</strong> less efficient than the optimal path.</span> If we abstract away the mowing, it’s really just a path formed by connecting nodes. The optimal path looks like this:"},{"type":"Result","value":{}},{"type":"text","value":"<span class=custom-humans>Surprisingly,</span> most humans are actually really good at finding solutions to pathfinding problems like this. The most popular example of pathfinding is the <a href=https://en.wikipedia.org/wiki/Travelling_salesman_problem target=_blank rel=noreferrer>Traveling Salesman Problem.</a>"},{"type":"Video","value":{"src":"assets/videos/tsp.mp4","preload":"true","autoplay":"true","muted":"true","loop":"true","figcaption":"From <a href=https://www.youtube.com/watch?v=BAejnwN4Ccw target=_blank rel=noreferrer>The Coding Train</a> with Daniel Shiffman"}},{"type":"text","value":"The premise is simple: a salesperson must visit a set of cities exactly once and return to their starting point, all while taking the shortest possible route. We see this all the time in the real world. From a pizza delivery driver planning their stops to a trick-or-treater maximizing their candy haul, finding the most efficient routes would be solving a version of this problem."},{"type":"text","value":"I <a href=https://docs.lib.purdue.edu/jps/vol1/iss1/4/ target=_blank rel=noreferrer>read a study</a> that found humans came impressively close to computer-calculated optimal solutions to this problem with fewer stops, achieving solutions just 11% less efficient than optimal for routes with 70+ stops."},{"type":"Img","value":{"src":"assets/images/study.png","alt":"A chart depicting how people perform on the Traveling Salesman Problem compared to the optimal path, which shows that people perform near-ideal at fewer nodes, with a slow increasing deviation as more nodes are added","figcaption":"Chart of findings from <a href=https://docs.lib.purdue.edu/jps/vol1/iss1/4/ target=_blank rel=noreferrer>this study</a>"}},{"type":"text","value":"<strong>I was curious; how well might humans perform on a similar optimization problem?</strong>"},{"type":"text","value":"But first, a <em>very quick</em> computer science primer. There are two main ways to tackle problems like these: <strong>algorithms</strong> that calculate the optimal path and <strong>heuristics</strong>—simple strategies for completing the task."},{"type":"text","value":"<strong>Algorithms</strong> solve the problem with an estimated optimal solution. But that can come at a cost as complexity grows. For example, brute force calculations for a salesperson visiting <strong>just 10 cities</strong> requires testing <strong>3.6 million</strong> possible routes, and that number explodes exponentially as more cities are added."},{"type":"text","value":"<strong>15 cities</strong> and we’re at <strong>1.3 trillion</strong> possible routes. 😱"},{"type":"text","value":"<strong>20 cities</strong> balloons to <strong>2.4 quintillion</strong> possible routes. 🤯"},{"type":"text","value":"<strong>Heuristics</strong> are a lot like human problem-solving; they aim for a good-enough strategy that solves the problem quickly. For example, choosing the closest city at each step isn’t perfect, but it’s much faster and usually close to the optimal route."},{"type":"text","value":"For our experiment, we’ll stick with heuristics."},{"type":"text","value":"<span class=you><strong>What type of computer-y brain do you have?</strong> Our sorting hat determined that your mowing style is most similar to the <strong class=sorting-hat>X</strong> heuristic.</span><span class=skip>So what type of computer-brain do most people have?</span>"},{"type":"text","value":"<mark>(graphic breakdown of all users and the most similar heuristic types)</mark>"},{"type":"text","value":"We chose to look at seven different heuristics for our lawn mower game, which is a problem more formally called <strong>Coverage Path Planning.</strong> To focus on your path planning vs. driving skills, we simplified the world into a grid of cells."},{"type":"text","value":"Each heuristic follows a different approach to visit each cell on the grid. To get a sense of their general style, here are all seven heuristics on an empty grid with no obstacles."},{"type":"text","value":"<mark>(graphic animate all 7, rate efficiency)</mark>"},{"type":"text","value":"But an open lawn or an empty grid isn’t that interesting. Things get more fun—and complex—when we introduce obstacles."},{"type":"text","value":"<mark>(graphic animate all 7, rate efficiency)</mark>"},{"type":"text","value":"But this is just one layout. We gave each heuristic 100 different layouts to see how they handled various situations."},{"type":"text","value":"<mark>(graphic some other viz, maybe high level results)</mark>"},{"type":"text","value":"Tk talk about complexity scores and Big-O notation. Why not."},{"type":"text","value":"Okay so that is the heuristics. Let’s get back to the humans. Here is a distribution of how well people did compared to the optimal solution."},{"type":"text","value":"<mark>(graphic breakdown of how optimal everyone performed / you)</mark>"},{"type":"text","value":"Tk explanation of why humans are good at this."},{"type":"text","value":"Tk wrap things up."},{"type":"text","value":"<mark>(graphic maybe a situation changer so they can go up against all 100 layouts from earlier)</mark>"}]}]}
32 changes: 32 additions & 0 deletions src/runes/localStores.svelte.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { browser } from "$app/environment";

export class LocalStore {
value = $state();
key = "";

constructor(key, value) {
this.key = key;
this.value = value;

if (browser) {
const item = localStorage.getItem(key);
if (item) this.value = this.deserialize(item);
}

$effect(() => {
localStorage.setItem(this.key, this.serialize(this.value));
});
}

serialize(value) {
return JSON.stringify(value);
}

deserialize(item) {
return JSON.parse(item);
}
}

export default function localStore(key, value) {
return new LocalStore(key, value);
}
2 changes: 1 addition & 1 deletion src/runes/misc.svelte.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const mode = $state({ game: true });
export const game = $state({ active: true, completed: false });

0 comments on commit c72ef32

Please sign in to comment.