From f93b0060d9917ed34275e07c82b5c4aeeb28eec1 Mon Sep 17 00:00:00 2001 From: Craig Laparo Date: Thu, 29 Aug 2024 13:05:10 -0500 Subject: [PATCH 1/8] Add note about unaccessed short URL expiration --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 0de6e4b3..b5f157df 100644 --- a/readme.md +++ b/readme.md @@ -250,7 +250,7 @@ The structure of the URL is as follows: `https://ptn.ninja/&=&=[...]` -To get a shortened URL, send a POST request to `https://url.ptn.ninja/short` with request body `{ ptn, params (optional) }` where `ptn` is a string, and `params` is an optional object containing any of the parameters below. If the request is valid, you'll receive the complete shortenend URL as plain text in response. +To get a shortened URL, send a POST request to `https://url.ptn.ninja/short` with request body `{ ptn, params (optional) }` where `ptn` is a string, and `params` is an optional object containing any of the parameters below. If the request is valid, you'll receive the complete shortenend URL as plain text in response. If the URL is not accessed within 30 days, it will be deleted. ### URL Parameters From 2224806c03bf244b5302888fe4c6b418fae2843d Mon Sep 17 00:00:00 2001 From: Craig Laparo Date: Thu, 29 Aug 2024 20:05:18 -0500 Subject: [PATCH 2/8] Comments --- src/components/drawers/BotSuggestions.vue | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/drawers/BotSuggestions.vue b/src/components/drawers/BotSuggestions.vue index 773fed64..b94b14ff 100644 --- a/src/components/drawers/BotSuggestions.vue +++ b/src/components/drawers/BotSuggestions.vue @@ -1095,8 +1095,13 @@ export default { }, botSettings: { handler(settings) { + // Save preferences this.$store.dispatch("ui/SET_UI", ["botSettings", settings]); + + // Update current position/bot hash this.botSettingsHash = this.hashBotSettings(settings); + + // Stop interactive analysis when switching bots if (settings.bot !== "tiltak" && this.tiltakInteractive.isEnabled) { this.tiltakInteractive.isEnabled = false; } From c87c8b19e221aa219a6888e38358dfd8877220ad Mon Sep 17 00:00:00 2001 From: Craig Laparo Date: Mon, 2 Sep 2024 17:53:25 -0500 Subject: [PATCH 3/8] Bug fix for Topaz (wasm) UX --- src/components/drawers/BotSuggestions.vue | 46 ++++++++++++----------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/components/drawers/BotSuggestions.vue b/src/components/drawers/BotSuggestions.vue index b94b14ff..85087637 100644 --- a/src/components/drawers/BotSuggestions.vue +++ b/src/components/drawers/BotSuggestions.vue @@ -202,35 +202,39 @@ - + /> + + + From d9f0befc72e393e410a4a83b1803cb00c3fff001 Mon Sep 17 00:00:00 2001 From: Craig Laparo Date: Mon, 2 Sep 2024 17:53:46 -0500 Subject: [PATCH 4/8] Tweak region markers --- src/components/drawers/BotSuggestions.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/drawers/BotSuggestions.vue b/src/components/drawers/BotSuggestions.vue index 85087637..8d136163 100644 --- a/src/components/drawers/BotSuggestions.vue +++ b/src/components/drawers/BotSuggestions.vue @@ -543,7 +543,7 @@ export default { } }, - // MARK: Tiltak Cloud + //#region Tiltak Cloud async analyzeGameTiltak() { if (this.isOffline || !this.game.ptn.branchPlies.length) { @@ -694,7 +694,7 @@ export default { return result; }, - // MARK: Tiltak WASM + //#region Tiltak WASM initTiltakInteractive(force = false) { if (force || !this.tiltakWorker) { @@ -927,7 +927,7 @@ export default { } }, - // MARK: Topaz + //#region Topaz initTopaz(force = false) { if (force || !this.topazWorker) { @@ -1027,7 +1027,7 @@ export default { } }, - // MARK: Init + //#region Init init() { // Load wasm bots From f2e70da74cdbece4bca58e304a77d3f4e6f04ae7 Mon Sep 17 00:00:00 2001 From: Craig Laparo Date: Mon, 2 Sep 2024 17:59:12 -0500 Subject: [PATCH 5/8] Bump version number --- package.json | 2 +- src/i18n/en-us/about.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 24d61a3c..3b49a288 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ptn-ninja", - "version": "3.4.19", + "version": "3.4.20", "description": "An editor and viewer for Portable Tak Notation", "productName": "PTN Ninja", "author": "Craig Laparo ", diff --git a/src/i18n/en-us/about.md b/src/i18n/en-us/about.md index 7325d11c..45fbcf79 100644 --- a/src/i18n/en-us/about.md +++ b/src/i18n/en-us/about.md @@ -1,6 +1,6 @@ # PTN Ninja -**Version [3.4.19](https://github.com/gruppler/PTN-Ninja/releases)** +**Version [3.4.20](https://github.com/gruppler/PTN-Ninja/releases)** This is an editor and viewer for [Portable Tak Notation (PTN)](https://ustak.org/portable-tak-notation/). It aims to be... From c7fb6bb5730ae669abcc9f87d0e1b5884fb0a057 Mon Sep 17 00:00:00 2001 From: Craig Laparo Date: Thu, 29 Aug 2024 21:08:05 -0500 Subject: [PATCH 6/8] Comments --- src/Game/base.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Game/base.js b/src/Game/base.js index a7b43167..5423caa7 100644 --- a/src/Game/base.js +++ b/src/Game/base.js @@ -78,6 +78,7 @@ export default class GameBase { return params; } + // #region Init init({ ptn, name, @@ -145,8 +146,10 @@ export default class GameBase { this.notes = {}; this.warnings = []; - // Parse HEAD + //#region Parse HEAD + try { + // Parse tags from PTN if (ptn) { let item, key; @@ -174,6 +177,7 @@ export default class GameBase { } } + // Parse tags from JSON if (tags) { each(tags, (value, key) => { if (value) { @@ -195,6 +199,7 @@ export default class GameBase { }); } + // Parse datetime if (this.tags.date) { this.datetime = Tag.toDate( this.tags.date.value, @@ -204,17 +209,21 @@ export default class GameBase { this.datetime = new Date(); } + // Get size if (this.tags.size) { this.size = this.tags.size.value; } else { if (this.tags.tps) { + // Derive from TPS this.size = this.tags.tps.value.size; this.tags.size = Tag.parse(`[Size "${this.size}"]`); } else { let error = "Missing board size"; if (ptn) { + // Fail if initializing from PTN throw new Error(error); } else { + // Use default size otherwise console.warn(error); this.warnings.push(error); this.size = this.defaultSize; @@ -223,10 +232,12 @@ export default class GameBase { } } + // Sanitize opening if (!this.tags.opening) { this.tags.opening = Tag.parse('[Opening "swap"]'); } + // Parse TPS if (this.tags.tps) { this.hasTPS = true; this.firstMoveNumber = this.tags.tps.value.linenum; @@ -271,8 +282,10 @@ export default class GameBase { this.updateConfig(); this.board = new Board(this, null, this.board ? this.board.output : null); - // Parse BODY + //#region Parse BODY + if (ptn) { + // Parse moves from PTN let item, ply; let branch = null; let move = new Move({ @@ -422,6 +435,7 @@ export default class GameBase { delete item.ptn; } } else if (moves) { + // Parse moves from JSON if (comments) { this.parseJSONComments(comments, -1); } @@ -431,6 +445,8 @@ export default class GameBase { return handleError(error); } + //#region Init Game + if (!this.moves[0]) { this.moves[0] = new Move({ game: this, @@ -451,6 +467,7 @@ export default class GameBase { this.name = this.generateName(); } + // Init TPS if (this.tags.tps || this.editingTPS) { this.board.doTPS(this.editingTPS); } @@ -518,6 +535,8 @@ export default class GameBase { } } + //#region Methods + get minState() { return this.board.minState; } @@ -735,6 +754,8 @@ export default class GameBase { } } + //#region Output + toString(options) { options = Object.assign( { From 06ec5f69120f4a3c7cf8167e62c2603ae09d3226 Mon Sep 17 00:00:00 2001 From: Craig Laparo Date: Mon, 2 Sep 2024 20:50:22 -0500 Subject: [PATCH 7/8] Relax clock format validation --- src/Game/PTN/Tag.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Game/PTN/Tag.js b/src/Game/PTN/Tag.js index ea85d928..82018bbd 100644 --- a/src/Game/PTN/Tag.js +++ b/src/Game/PTN/Tag.js @@ -36,8 +36,7 @@ export const formats = { flats: /^\d+$/, flats1: /^\d+$/, flats2: /^\d+$/, - clock: - /^\d+min(\+\d+sec)$|^((((\d\s+)?\d\d?:)?\d\d?:)?\d\d?\s*)?(\+(((\d\s+)?\d\d?:)?\d\d?:)?\d\d?)?$/, + clock: /^[^"]+$/, date: /^\d{4}\.\d\d?\.\d\d?$/, event: /^[^"]+$/, komi: /^-?\d*(\.5)?$/, From af05aaae6846c33f6729cd874ad45414dd79b2b6 Mon Sep 17 00:00:00 2001 From: Craig Laparo Date: Mon, 2 Sep 2024 21:27:15 -0500 Subject: [PATCH 8/8] Support multi-game .ptn files --- src/Game/base.js | 7 +++++ src/store/game/actions.js | 60 ++++++++++++++++++++++----------------- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/src/Game/base.js b/src/Game/base.js index 5423caa7..c31a9b2d 100644 --- a/src/Game/base.js +++ b/src/Game/base.js @@ -78,6 +78,13 @@ export default class GameBase { return params; } + // Split multi-game PTN into separate games + static split(ptn) { + return ptn.match( + /^\s*(\[\w+\s"[^"]*"\]\s*)+((\{[^}]*\})*[/.\s\da-hCFRS!?"'><+-]+)+/gm + ); + } + // #region Init init({ ptn, diff --git a/src/store/game/actions.js b/src/store/game/actions.js index 26b62f8f..27cfe546 100644 --- a/src/store/game/actions.js +++ b/src/store/game/actions.js @@ -444,11 +444,41 @@ export const OPEN_FILES = async function ({ dispatch, state }, files) { resolve; } }; + const onInit = (game) => { games.push(game); finish(); }; + const parseGame = (ptn, name) => { + const onError = (error, plyID) => { + console.warn( + `Encountered an error in "${name}" at plyID:`, + plyID, + error + ); + notifyError(`${name}: ${error.message}`); + finish(); + }; + + const index = state.list.findIndex((g) => g.name === name); + if (index < 0 || this.state.ui.openDuplicate !== "replace") { + try { + new Game({ + ptn, + name, + onError, + onInit, + }); + } catch (error) { + console.error("Invalid game:", name, error); + } + } else { + dispatch("REPLACE_GAME", { index, ptn }); + finish(); + } + }; + files = Array.from(files); files = files.filter((file) => file && /(\.ptn|\.txt)+$/i.test(file.name)); if (!files.length) { @@ -462,32 +492,10 @@ export const OPEN_FILES = async function ({ dispatch, state }, files) { let reader = new FileReader(); reader.onload = (event) => { const name = file.name.replace(/(\.ptn|\.txt)+$/, ""); - const index = state.list.findIndex((g) => g.name === name); - const ptn = event.target.result; - const onError = (error, plyID) => { - console.warn( - `Encountered an error in "${name}" at plyID:`, - plyID, - error - ); - notifyError(`${name}: ${error.message}`); - finish(); - }; - if (index < 0 || this.state.ui.openDuplicate !== "replace") { - try { - new Game({ - ptn, - name, - onError, - onInit, - }); - } catch (error) { - console.error("Invalid game:", name, error); - } - } else { - dispatch("REPLACE_GAME", { index, ptn }); - finish(); - } + const games = Game.split(event.target.result); + games.forEach((ptn, i) => { + parseGame(ptn, `${name} - Game ${i + 1}`); + }); }; reader.onerror = notifyError; reader.readAsText(file);