Skip to content

Commit

Permalink
Added dot matrix dojo type
Browse files Browse the repository at this point in the history
  • Loading branch information
Perlkonig committed Jul 14, 2022
1 parent b175f99 commit bebdefd
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 30 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

* Added peer count to new peer list.
* Added the dot matrix dojo type.

### Removed

* Stripped out the inter-client chat feature. Players have to find each other using external means anyway. The intent is just to make it possible to moderate a remote synchronous game, and it is expected that players will be on Discord or something. It also frees up more horizontal space for koans.
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ Once connected, players have access to a basic text chat window and the rest of

## Dojo Types

There are four dojo types currently supported:
There are five dojo types currently supported:

* plain text (koans are displayed as entered)
* images (drag and drop square image files)
* math (powered by [KaTeX](https://katex.org/), using TeX/LaTeX notation)
* 1d pyramids (custom notation for creating one-dimensional koans with [Looney pyramids](https://www.looneylabs.com/pyramids-home))
* 1d pyramids (one-dimensional koans with [Looney pyramids](https://www.looneylabs.com/pyramids-home))
* dot matrix (coloured dots on rectangular grids)

## Caveats

Expand Down
19 changes: 14 additions & 5 deletions src/lib/Game/ActionBar/AddKoan.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
placeholder = "Enter in TeX or LaTeX format, without delimiters"
} else if ($game.koanType === "1dpyramids") {
placeholder = "Enter a series of pyramid designations separated by whitespace";
} else if ($game.koanType === "dotmatrix") {
placeholder = "Enter width, height, and then a string of digits";
}
let files: FileList;
Expand Down Expand Up @@ -67,6 +69,8 @@
let typeDesc = $game.koanType.charAt(0).toUpperCase() + $game.koanType.slice(1);
if ($game.koanType === "1dpyramids") {
typeDesc = "1D Pyramid";
} else if ($game.koanType === "dotmatrix") {
typeDesc = "Dot Matrix";
}
const colours = new Map<string, string>([
Expand Down Expand Up @@ -120,11 +124,16 @@
Images must be square.
</p>
{:else if $game.koanType === "1dpyramids"}
<div class="help">
<p>
COLOUR + SIZE + DIRECTION (case insensitive); for example "RD1", "BN2E", "VT3S"
</p>
</div>
<p class="help">
COLOUR + SIZE + DIRECTION (case insensitive); for example "RD1", "BN2E", "VT3S"
</p>
{:else if $game.koanType === "dotmatrix"}
<p class="help">
WIDTH,HEIGHT,CELLS (e.g., 2,2,1001)
</p>
<p class="help">
The digit <code>0</code> is an empty cell, <code>1</code> is black, and the digits <code>2–9</code> give a sequence of different colours.
</p>
{/if}
</div>
{#if $game.koanType === "1dpyramids"}
Expand Down
6 changes: 5 additions & 1 deletion src/lib/Game/ActionBar/Master.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
let koanType = "text";
const chooseType = () => {
if ( (koanType === "text") || (koanType === "image") || (koanType === "math") || (koanType === "1dpyramids") ) {
if ( (koanType === "text") || (koanType === "image") || (koanType === "math") || (koanType === "1dpyramids") || (koanType === "dotmatrix") ) {
$game.koanType = koanType;
$game = $game;
pushGame();
Expand Down Expand Up @@ -147,6 +147,10 @@
<input type=radio bind:group={koanType} name="koanType" value={"1dpyramids"}>
1D pyramid koans
</label><br>
<label class="radio">
<input type=radio bind:group={koanType} name="koanType" value={"dotmatrix"}>
Dot matrix koans
</label><br>
<label class="radio">
<input type=radio bind:group={koanType} name="koanType" value={"math"}>
Math koans (powered by <a href="https://katex.org/">KaTeX</a>)
Expand Down
97 changes: 78 additions & 19 deletions src/lib/Game/ViewKoan.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import { peer } from "@/stores/writePeerObj";
import { peers } from "@/stores/writePeers";
import type { ZendoGameMessages } from "@/schemas/messages";
import CurrPlayer from "./ActionBar/CurrPlayer.svelte";
// You can provide either a number or a string, but not both.
// Passing a number assumes the koan already exists in the game object.
Expand Down Expand Up @@ -46,25 +45,25 @@ import CurrPlayer from "./ActionBar/CurrPlayer.svelte";
}
}
// Doing this the dirty but straightforward way of just cobbling together an SVG from pieces.
const symbols = new Map<string,[string,string]>([
["3", [`<symbol viewBox="0 0 180 180" id=""><polygon points="90,2.5 40,177.5 140,177.5" data-playerfill="true" stroke-opacity="0.5" stroke-width="2" stroke="#000000"></polygon><circle r="5" cx="52.5" cy="167.5" fill="#000000" opacity="0.75"></circle><circle r="5" cx="67.5" cy="167.5" fill="#000000" opacity="0.75"></circle><circle r="5" cx="82.5" cy="167.5" fill="#000000" opacity="0.75"></circle></symbol>`, `<symbol viewBox="0 0 180 180" id=""><rect width="100" height="100" data-playerfill="true" stroke-opacity="0.5" stroke-width="2" stroke="#000000" x="40" y="40"></rect><line x1="40" y1="140" x2="140" y2="40" stroke-opacity="0.25" stroke-width="1" stroke="#000000"></line><line x1="40" y1="40" x2="140" y2="140" stroke-opacity="0.25" stroke-width="1" stroke="#000000"></line><circle r="2" cx="90" cy="90" fill="#000000" opacity="0.25"></circle><line x1="50" y1="136" x2="60" y2="136" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="65" y1="136" x2="75" y2="136" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="80" y1="136" x2="90" y2="136" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="130" y1="44" x2="120" y2="44" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="115" y1="44" x2="105" y2="44" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="100" y1="44" x2="90" y2="44" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="44" y1="50" x2="44" y2="60" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="44" y1="65" x2="44" y2="75" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="44" y1="80" x2="44" y2="90" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="136" y1="130" x2="136" y2="120" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="136" y1="115" x2="136" y2="105" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="136" y1="100" x2="136" y2="90" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line></symbol>`]],
["2", [`<symbol viewBox="0 0 180 180" id=""><polygon points="90,21.25 50.9375,158.75 129.0625,158.75" data-playerfill="true" stroke-opacity="0.5" stroke-width="2" stroke="#000000"></polygon><circle r="5" cx="63.4375" cy="148.75" fill="#000000" opacity="0.75"></circle><circle r="5" cx="78.4375" cy="148.75" fill="#000000" opacity="0.75"></circle></symbol>`, `<symbol viewBox="0 0 180 180" id=""><rect width="180" height="180" fill="none"></rect><rect width="78.125" height="78.125" data-playerfill="true" stroke-opacity="0.5" stroke-width="2" stroke="#000000" x="50.9375" y="50.9375"></rect><line x1="50.9375" y1="129.0625" x2="129.0625" y2="50.9375" stroke-opacity="0.25" stroke-width="1" stroke="#000000"></line><line x1="50.9375" y1="50.9375" x2="129.0625" y2="129.0625" stroke-opacity="0.25" stroke-width="1" stroke="#000000"></line><circle r="2" cx="90" cy="90" fill="#000000" opacity="0.25"></circle><line x1="60.9375" y1="125.0625" x2="70.9375" y2="125.0625" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="75.9375" y1="125.0625" x2="85.9375" y2="125.0625" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="119.0625" y1="54.9375" x2="109.0625" y2="54.9375" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="104.0625" y1="54.9375" x2="94.0625" y2="54.9375" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="54.9375" y1="60.9375" x2="54.9375" y2="70.9375" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="54.9375" y1="75.9375" x2="54.9375" y2="85.9375" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="125.0625" y1="119.0625" x2="125.0625" y2="109.0625" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="125.0625" y1="104.0625" x2="125.0625" y2="94.0625" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line></symbol>`]],
["1", [`<symbol viewBox="0 0 180 180" id=""><polygon points="90,40 61.875,140 118.125,140" data-playerfill="true" stroke-opacity="0.5" stroke-width="2" stroke="#000000"></polygon><circle r="5" cx="74.375" cy="130" fill="#000000" opacity="0.75"></circle></symbol>`, `<symbol viewBox="0 0 180 180" id=""><rect width="180" height="180" fill="none"></rect><rect width="56.25" height="56.25" data-playerfill="true" stroke-opacity="0.5" stroke-width="2" stroke="#000000" x="61.875" y="61.875"></rect><line x1="61.875" y1="118.125" x2="118.125" y2="61.875" stroke-opacity="0.25" stroke-width="1" stroke="#000000"></line><line x1="61.875" y1="61.875" x2="118.125" y2="118.125" stroke-opacity="0.25" stroke-width="1" stroke="#000000"></line><circle r="2" cx="90" cy="90" fill="#000000" opacity="0.25"></circle><line x1="71.875" y1="114.125" x2="81.875" y2="114.125" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="108.125" y1="65.875" x2="98.125" y2="65.875" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="65.875" y1="71.875" x2="65.875" y2="81.875" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="114.125" y1="108.125" x2="114.125" y2="98.125" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line></symbol>`]],
]);
const colours = new Map<string, string>([
["RD", "#e41a1c"],
["BU", "#377eb8"],
["GN", "#4daf4a"],
["YE", "#ffff33"],
["VT", "#984ea3"],
["OG", "#ff7f00"],
["BN", "#a65628"],
["PK", "#f781bf"],
["GY", "#999999"],
["WH", "#ffffff"],
]);
const process1dSvg = (): string => {
// Doing this the dirty but straightforward way of just cobbling together an SVG from pieces.
const symbols = new Map<string,[string,string]>([
["3", [`<symbol viewBox="0 0 180 180" id=""><polygon points="90,2.5 40,177.5 140,177.5" data-playerfill="true" stroke-opacity="0.5" stroke-width="2" stroke="#000000"></polygon><circle r="5" cx="52.5" cy="167.5" fill="#000000" opacity="0.75"></circle><circle r="5" cx="67.5" cy="167.5" fill="#000000" opacity="0.75"></circle><circle r="5" cx="82.5" cy="167.5" fill="#000000" opacity="0.75"></circle></symbol>`, `<symbol viewBox="0 0 180 180" id=""><rect width="100" height="100" data-playerfill="true" stroke-opacity="0.5" stroke-width="2" stroke="#000000" x="40" y="40"></rect><line x1="40" y1="140" x2="140" y2="40" stroke-opacity="0.25" stroke-width="1" stroke="#000000"></line><line x1="40" y1="40" x2="140" y2="140" stroke-opacity="0.25" stroke-width="1" stroke="#000000"></line><circle r="2" cx="90" cy="90" fill="#000000" opacity="0.25"></circle><line x1="50" y1="136" x2="60" y2="136" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="65" y1="136" x2="75" y2="136" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="80" y1="136" x2="90" y2="136" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="130" y1="44" x2="120" y2="44" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="115" y1="44" x2="105" y2="44" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="100" y1="44" x2="90" y2="44" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="44" y1="50" x2="44" y2="60" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="44" y1="65" x2="44" y2="75" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="44" y1="80" x2="44" y2="90" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="136" y1="130" x2="136" y2="120" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="136" y1="115" x2="136" y2="105" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="136" y1="100" x2="136" y2="90" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line></symbol>`]],
["2", [`<symbol viewBox="0 0 180 180" id=""><polygon points="90,21.25 50.9375,158.75 129.0625,158.75" data-playerfill="true" stroke-opacity="0.5" stroke-width="2" stroke="#000000"></polygon><circle r="5" cx="63.4375" cy="148.75" fill="#000000" opacity="0.75"></circle><circle r="5" cx="78.4375" cy="148.75" fill="#000000" opacity="0.75"></circle></symbol>`, `<symbol viewBox="0 0 180 180" id=""><rect width="180" height="180" fill="none"></rect><rect width="78.125" height="78.125" data-playerfill="true" stroke-opacity="0.5" stroke-width="2" stroke="#000000" x="50.9375" y="50.9375"></rect><line x1="50.9375" y1="129.0625" x2="129.0625" y2="50.9375" stroke-opacity="0.25" stroke-width="1" stroke="#000000"></line><line x1="50.9375" y1="50.9375" x2="129.0625" y2="129.0625" stroke-opacity="0.25" stroke-width="1" stroke="#000000"></line><circle r="2" cx="90" cy="90" fill="#000000" opacity="0.25"></circle><line x1="60.9375" y1="125.0625" x2="70.9375" y2="125.0625" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="75.9375" y1="125.0625" x2="85.9375" y2="125.0625" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="119.0625" y1="54.9375" x2="109.0625" y2="54.9375" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="104.0625" y1="54.9375" x2="94.0625" y2="54.9375" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="54.9375" y1="60.9375" x2="54.9375" y2="70.9375" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="54.9375" y1="75.9375" x2="54.9375" y2="85.9375" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="125.0625" y1="119.0625" x2="125.0625" y2="109.0625" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="125.0625" y1="104.0625" x2="125.0625" y2="94.0625" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line></symbol>`]],
["1", [`<symbol viewBox="0 0 180 180" id=""><polygon points="90,40 61.875,140 118.125,140" data-playerfill="true" stroke-opacity="0.5" stroke-width="2" stroke="#000000"></polygon><circle r="5" cx="74.375" cy="130" fill="#000000" opacity="0.75"></circle></symbol>`, `<symbol viewBox="0 0 180 180" id=""><rect width="180" height="180" fill="none"></rect><rect width="56.25" height="56.25" data-playerfill="true" stroke-opacity="0.5" stroke-width="2" stroke="#000000" x="61.875" y="61.875"></rect><line x1="61.875" y1="118.125" x2="118.125" y2="61.875" stroke-opacity="0.25" stroke-width="1" stroke="#000000"></line><line x1="61.875" y1="61.875" x2="118.125" y2="118.125" stroke-opacity="0.25" stroke-width="1" stroke="#000000"></line><circle r="2" cx="90" cy="90" fill="#000000" opacity="0.25"></circle><line x1="71.875" y1="114.125" x2="81.875" y2="114.125" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="108.125" y1="65.875" x2="98.125" y2="65.875" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="65.875" y1="71.875" x2="65.875" y2="81.875" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line><line x1="114.125" y1="108.125" x2="114.125" y2="98.125" stroke-opacity="0.75" stroke-width="2" stroke="#000000"></line></symbol>`]],
]);
const colours = new Map<string, string>([
["RD", "#e41a1c"],
["BU", "#377eb8"],
["GN", "#4daf4a"],
["YE", "#ffff33"],
["VT", "#984ea3"],
["OG", "#ff7f00"],
["BN", "#a65628"],
["PK", "#f781bf"],
["GY", "#999999"],
["WH", "#ffffff"],
]);
const pieces = koanStr.toUpperCase().split(/\s+/);
if ( (koanStr === undefined) || (koanStr === "") || (pieces.length === 0) ) {
return "";
Expand Down Expand Up @@ -142,9 +141,65 @@ import CurrPlayer from "./ActionBar/CurrPlayer.svelte";
return svgStr;
};
const processDotMatrixSVG = (): string => {
const colours: string[] = [
"#000000",
"#e41a1c",
"#4daf4a",
"#377eb8",
"#ffff33",
"#984ea3",
"#ff7f00",
"#a65628",
"#f781bf",
];
koanStr = koanStr.replaceAll(/\s/g, "");
const parts = koanStr.split(",");
if ( (parts.length < 2) || (parts.length > 3) ) {
return "";
}
const width = parseInt(parts[0], 10);
const height = parseInt(parts[1], 10);
if ( (isNaN(width)) || (isNaN(height)) ) {
return "";
}
let svgStr = `<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" viewBox="-7 -7 ${(width * 12) + 2} ${(height * 12) + 2}" width="100%" height="100%"><title>${koanStr}</title><defs>`;
// Just add all nine colours every time, used or not
for (let i = 0; i < colours.length; i++) {
svgStr += `<symbol viewBox="-5 -5 10 10" id="Dot${i + 1}"><circle r="4" cx="0" cy="0" fill="${colours[i]}" stroke="${colours[i]}"></symbol>`;
}
svgStr += `</defs>`;
// draw the lines
for (let i = 0; i <= width; i++) {
svgStr += `<line x1="${-6 + (i * 12)}" y1="-6" x2="${-6 + (i * 12)}" y2="${-6 + (height * 12)}" stroke-width="0.25" stroke="#000000"></line>`;
}
for (let i = 0; i <= height; i++) {
svgStr += `<line x1="-6" y1="${-6 + (i * 12)}" x2="${-6 + (width * 12)}" y2="${-6 + (i * 12)}" stroke-width="0.25" stroke="#000000"></line>`;
}
// place the pieces
if (parts.length === 3) {
for (let i = 0; i < parts[2].length; i++) {
const digit = parts[2][i];
const num = parseInt(digit, 10);
if ( (isNaN(num)) || (num === 0) ) { continue; }
const row = Math.floor(i / width);
const col = i % width;
svgStr += `<use href="#Dot${digit}" x="${(col * 12) - 5.5}" y="${(row * 12) - 5.5}" height="11" width="11"></use>`;
}
}
svgStr += `</svg>`;
return svgStr;
};
let svgResults: string;
if ($game.koanType === "1dpyramids") {
svgResults = process1dSvg();
} else if ($game.koanType === "dotmatrix") {
svgResults = processDotMatrixSVG();
}
let modalDelete = "";
Expand Down Expand Up @@ -195,6 +250,10 @@ import CurrPlayer from "./ActionBar/CurrPlayer.svelte";
<figure class="koan pyramidkoan">
{@html svgResults}
</figure>
{:else if $game.koanType === "dotmatrix"}
<figure class="koan dotmatrixkoan">
{@html svgResults}
</figure>
{:else}
<figure class="koan text-koan content">
<p>{koanStr}</p>
Expand Down
4 changes: 4 additions & 0 deletions src/lib/Game/Welcome.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
<p>This is an image dojo.</p>
{:else if $game.koanType === "math"}
<p>This is a math dojo (powered by <a href="https://katex.org/">KaTeX</a>).</p>
{:else if $game.koanType === "1dpyramids"}
<p>This is a 1D pyramid dojo.</p>
{:else if $game.koanType === "dotmatrix"}
<p>This is a dot matrix dojo.</p>
{:else}
<p>This is a text dojo.</p>
{/if}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/PeerList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@

<div class="card" id="peerBox">
<header class="card-header">
<p class="card-header-title">Connected Peers</p>
<p class="card-header-title">Connected Peers ({$peers.length})</p>
<button class="card-header-icon" aria-label="toggle peers" title="toggle peers" on:click="{() => expandPeers = !expandPeers}">
{#if expandPeers}
<span class="icon">
Expand Down
Loading

0 comments on commit bebdefd

Please sign in to comment.