diff --git a/minigui/README.md b/minigui/README.md index 1bc9cd544..2d8c2d26b 100644 --- a/minigui/README.md +++ b/minigui/README.md @@ -310,3 +310,16 @@ stdout: = D4 - *fails*: If the id doesn't correspond to a position played in the game or one of its variations. - *comments*: Required only for Minigui's study mode. + + +### Hacking + + +e.g. in study.ts, change the last line to something like + +``` +(window as any)['app'] = new ExploreApp(); +``` + +This will put the application in a global variable named "app"; you need to cast +the window object to the "any" type to silence the TS compiler. diff --git a/minigui/board.ts b/minigui/board.ts index bc171b4cc..fdf7e8c38 100644 --- a/minigui/board.ts +++ b/minigui/board.ts @@ -284,7 +284,7 @@ class ClickableBoard extends Board { drawImpl() { super.drawImpl(); let p = this.enabled ? this.p : null; - this.ctx.canvas.style.cursor = p ? 'pointer' : null; + this.ctx.canvas.style.cursor = p ? 'pointer' : ''; if (p) { this.drawStones([p], this.position.toPlay, 0.6); } diff --git a/minigui/layer.ts b/minigui/layer.ts index f1503ac1a..147244d40 100644 --- a/minigui/layer.ts +++ b/minigui/layer.ts @@ -458,11 +458,24 @@ namespace Variation { class Annotations extends Layer { - private annotations = new Map(); + private annotations: Annotation[] = []; + private _showDivergence = false; + + get showDivergence() : boolean { + return this._showDivergence; + } + set showDivergence(x: boolean) { + if (x == this._showDivergence) { + return; + } + this._showDivergence = x; + this.update(new Set(["annotations"])); + this.board.draw(); + } clear() { - if (this.annotations.size > 0) { - this.annotations.clear(); + if (this.annotations.length > 0) { + this.annotations = []; this.board.draw(); } } @@ -473,41 +486,34 @@ class Annotations extends Layer { } let position = this.board.position; - this.annotations.clear(); - for (let annotation of position.annotations) { - let byShape = this.annotations.get(annotation.shape); - if (byShape === undefined) { - byShape = []; - this.annotations.set(annotation.shape, byShape); - } - byShape.push(annotation); - } + // TODO this.annotations could be a map keyed by coordinate. + this.annotations = position.annotations; return true; } draw() { - if (this.annotations.size == 0) { + if (this.annotations.length == 0) { return; } let sr = this.board.stoneRadius; - let pr = pixelRatio(); let ctx = this.board.ctx; - ctx.lineCap = 'round'; - this.annotations.forEach((annotations: Annotation[], shape: Annotation.Shape) => { - switch (shape) { - case Annotation.Shape.Dot: - for (let annotation of annotations) { - let c = this.boardToCanvas(annotation.p.row, annotation.p.col); - ctx.fillStyle = annotation.colors[0]; - ctx.beginPath(); - ctx.arc(c.x + 0.5, c.y + 0.5, 0.16 * sr, 0, 2 * Math.PI); - ctx.fill(); - } - break; + let textHeight = Math.floor(0.8 * sr); + ctx.font = `${textHeight}px sans-serif`; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + + for (let annotation of this.annotations) { + ctx.fillStyle = annotation.colors[0]; + let c = this.boardToCanvas(annotation.p.row, annotation.p.col); + if (annotation.label == "●"){ + ctx.fillText(annotation.label, c.x, c.y); } - }); + else if (this.showDivergence) { + ctx.fillText(annotation.label, c.x, c.y); + } + } } } diff --git a/minigui/position.ts b/minigui/position.ts index 3ec5bc06a..7af83dbb6 100644 --- a/minigui/position.ts +++ b/minigui/position.ts @@ -15,15 +15,9 @@ import {Color, Move, N, Nullable, Point, moveIsPoint, movesEqual, otherColor, stonesEqual, toGtp} from './base' import * as util from './util' -namespace Annotation { - export enum Shape { - Dot, - } -} - interface Annotation { p: Point; - shape: Annotation.Shape; + label: string; colors: string[]; } @@ -100,10 +94,10 @@ class Position { if (moveIsPoint(this.lastMove)) { this.annotations.push({ p: this.lastMove, - shape: Annotation.Shape.Dot, + label: "●", colors: ['#ef6c02'], }); - } + } } addChild(p: Position) { @@ -125,6 +119,34 @@ class Position { // Create a new child. p.isMainLine = this.isMainLine && this.children.length == 0; p.parent = this; + + // If it is not on the main line, prepare the move number annotations. + if (! p.isMainLine) { + let result: Move[] = []; + let node: Nullable + for (node = p; node && !node.isMainLine; node = node.parent) { + if (node.lastMove == null) { break; } + result.push(node.lastMove); + } + result.reverse(); + let playedCount = new Uint16Array(N * N); + for (let i=0; i < result.length - 1; ++i) { + let move = result[i]; + + if (moveIsPoint(move)) { + let idx = move.row * N + move.col; + let count = ++playedCount[idx]; + if (count != 1) { + continue; + } + p.annotations.push({ + p: move, // 'p' here for 'point'. + label: (i+1).toString(), + colors: ['#999999'], + }); + } + } + } this.children.push(p); } diff --git a/minigui/static/board.js b/minigui/static/board.js index 1ae7b0696..c7158773d 100644 --- a/minigui/static/board.js +++ b/minigui/static/board.js @@ -214,7 +214,7 @@ define(["require", "exports", "./base", "./layer", "./util", "./view"], function drawImpl() { super.drawImpl(); let p = this.enabled ? this.p : null; - this.ctx.canvas.style.cursor = p ? 'pointer' : null; + this.ctx.canvas.style.cursor = p ? 'pointer' : ''; if (p) { this.drawStones([p], this.position.toPlay, 0.6); } diff --git a/minigui/static/layer.js b/minigui/static/layer.js index ebbd76b6c..1c589765b 100644 --- a/minigui/static/layer.js +++ b/minigui/static/layer.js @@ -1,4 +1,4 @@ -define(["require", "exports", "./position", "./base", "./util"], function (require, exports, position_1, base_1, util_1) { +define(["require", "exports", "./base", "./util"], function (require, exports, base_1, util_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const STAR_POINTS = { @@ -382,11 +382,23 @@ define(["require", "exports", "./position", "./base", "./util"], function (requi class Annotations extends Layer { constructor() { super(...arguments); - this.annotations = new Map(); + this.annotations = []; + this._showDivergence = false; + } + get showDivergence() { + return this._showDivergence; + } + set showDivergence(x) { + if (x == this._showDivergence) { + return; + } + this._showDivergence = x; + this.update(new Set(["annotations"])); + this.board.draw(); } clear() { - if (this.annotations.size > 0) { - this.annotations.clear(); + if (this.annotations.length > 0) { + this.annotations = []; this.board.draw(); } } @@ -395,38 +407,29 @@ define(["require", "exports", "./position", "./base", "./util"], function (requi return false; } let position = this.board.position; - this.annotations.clear(); - for (let annotation of position.annotations) { - let byShape = this.annotations.get(annotation.shape); - if (byShape === undefined) { - byShape = []; - this.annotations.set(annotation.shape, byShape); - } - byShape.push(annotation); - } + this.annotations = position.annotations; return true; } draw() { - if (this.annotations.size == 0) { + if (this.annotations.length == 0) { return; } let sr = this.board.stoneRadius; - let pr = util_1.pixelRatio(); let ctx = this.board.ctx; - ctx.lineCap = 'round'; - this.annotations.forEach((annotations, shape) => { - switch (shape) { - case position_1.Annotation.Shape.Dot: - for (let annotation of annotations) { - let c = this.boardToCanvas(annotation.p.row, annotation.p.col); - ctx.fillStyle = annotation.colors[0]; - ctx.beginPath(); - ctx.arc(c.x + 0.5, c.y + 0.5, 0.16 * sr, 0, 2 * Math.PI); - ctx.fill(); - } - break; + let textHeight = Math.floor(0.8 * sr); + ctx.font = `${textHeight}px sans-serif`; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + for (let annotation of this.annotations) { + ctx.fillStyle = annotation.colors[0]; + let c = this.boardToCanvas(annotation.p.row, annotation.p.col); + if (annotation.label == "●") { + ctx.fillText(annotation.label, c.x, c.y); } - }); + else if (this.showDivergence) { + ctx.fillText(annotation.label, c.x, c.y); + } + } } } exports.Annotations = Annotations; diff --git a/minigui/static/position.js b/minigui/static/position.js index 0ab9fa1fc..a8f4a61e0 100644 --- a/minigui/static/position.js +++ b/minigui/static/position.js @@ -1,14 +1,6 @@ define(["require", "exports", "./base", "./util"], function (require, exports, base_1, util) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); - var Annotation; - (function (Annotation) { - let Shape; - (function (Shape) { - Shape[Shape["Dot"] = 0] = "Dot"; - })(Shape = Annotation.Shape || (Annotation.Shape = {})); - })(Annotation || (Annotation = {})); - exports.Annotation = Annotation; class Position { constructor(j) { this.parent = null; @@ -62,7 +54,7 @@ define(["require", "exports", "./base", "./util"], function (require, exports, b if (base_1.moveIsPoint(this.lastMove)) { this.annotations.push({ p: this.lastMove, - shape: Annotation.Shape.Dot, + label: "●", colors: ['#ef6c02'], }); } @@ -81,6 +73,33 @@ define(["require", "exports", "./base", "./util"], function (require, exports, b } p.isMainLine = this.isMainLine && this.children.length == 0; p.parent = this; + if (!p.isMainLine) { + let result = []; + let node; + for (node = p; node && !node.isMainLine; node = node.parent) { + if (node.lastMove == null) { + break; + } + result.push(node.lastMove); + } + result.reverse(); + let playedCount = new Uint16Array(base_1.N * base_1.N); + for (let i = 0; i < result.length - 1; ++i) { + let move = result[i]; + if (base_1.moveIsPoint(move)) { + let idx = move.row * base_1.N + move.col; + let count = ++playedCount[idx]; + if (count != 1) { + continue; + } + p.annotations.push({ + p: move, + label: (i + 1).toString(), + colors: ['#999999'], + }); + } + } + } this.children.push(p); } getChild(move) { diff --git a/minigui/static/study.js b/minigui/static/study.js index 59ba3a78e..1bd5b9db3 100644 --- a/minigui/static/study.js +++ b/minigui/static/study.js @@ -7,11 +7,12 @@ define(["require", "exports", "./app", "./base", "./board", "./layer", "./log", this.gtp = gtp; this._highlightedNextMove = null; this.searchLyr = new lyr.Search(); + this.annoLyr = new lyr.Annotations(); this.addLayers([ new lyr.Label(), new lyr.BoardStones(), this.searchLyr, - new lyr.Annotations() + this.annoLyr ]); this.enabled = true; } @@ -82,6 +83,7 @@ define(["require", "exports", "./app", "./base", "./board", "./layer", "./log", this.variationTree = new variation_tree_1.VariationTree('tree'); this.log = new log_1.Log('log', 'console'); this.showSearch = true; + this.showDiverge = false; this.showConsole = false; this.moveElem = util_1.getElement('move'); this.commentElem = util_1.getElement('comment'); @@ -184,6 +186,9 @@ define(["require", "exports", "./app", "./base", "./board", "./layer", "./log", case 'End': this.goForward(Infinity); break; + case 'v': + this.toggleNumberVariations(); + break; } }); window.addEventListener('wheel', (e) => { @@ -361,6 +366,9 @@ define(["require", "exports", "./app", "./base", "./board", "./layer", "./log", this.searchElem.innerText = 'Show search'; } } + toggleNumberVariations() { + this.board.annoLyr.showDivergence = !this.board.annoLyr.showDivergence; + } uploadTmpFile(contents) { return fetch('write_tmp_file', { method: 'POST', diff --git a/minigui/study.ts b/minigui/study.ts index fc8d9d430..01fb6e08d 100644 --- a/minigui/study.ts +++ b/minigui/study.ts @@ -47,16 +47,20 @@ class ExploreBoard extends ClickableBoard { } private searchLyr: lyr.Search; + public annoLyr: lyr.Annotations; + constructor(parentElemId: string, position: Position, private gtp: Socket) { super(parentElemId, position, []); this.searchLyr = new lyr.Search(); + this.annoLyr = new lyr.Annotations(); + this.addLayers([ new lyr.Label(), new lyr.BoardStones(), this.searchLyr, - new lyr.Annotations()]); + this.annoLyr]); this.enabled = true; } @@ -118,6 +122,7 @@ class ExploreApp extends App { private variationTree = new VariationTree('tree'); private log = new Log('log', 'console'); private showSearch = true; + private showDiverge = false; private showConsole = false; private moveElem = getElement('move'); private commentElem = getElement('comment'); @@ -246,6 +251,9 @@ class ExploreApp extends App { case 'End': this.goForward(Infinity); break; + case 'v': + this.toggleNumberVariations(); + break; } }); @@ -458,6 +466,10 @@ class ExploreApp extends App { } } + private toggleNumberVariations() { + this.board.annoLyr.showDivergence = !this.board.annoLyr.showDivergence; + } + private uploadTmpFile(contents: string) { return fetch('write_tmp_file', { method: 'POST',