UNPKG

ai-for-shogi-like-games

Version:

javascript library of A.I. algorithms for Shogi-like games

177 lines (155 loc) 6.92 kB
// @flow 'use strict'; import _ from 'lodash'; import assert from 'assert'; type F<V,E> = (n: Node<V, E>, parentN: ?Node<V, E>, birthEdge: ?E)=> void; type FV<V,E> = (n: Node<V, E>)=> boolean; type ValuePrinter<V> = (v: V)=>string; const TREE_NODE_ID_SYMBOL_KEY: string = 'mjb44-NODE-id'; class Node<V, E> { value: V; adornment: any; children: ?Map<E, Node<V,E>>; constructor(value: V) { this.value = value; this.adornment = null; this.children = null; } isAdorned(): boolean { const v = this.adornment!==null; if (false) // $SuppressFlowFinding: access of computed property/element. Indexable signature not found in ... console.log(`isAdorned called on node [${this[Symbol.for(TREE_NODE_ID_SYMBOL_KEY)]}], returning: ${v}`); return v; } allChildrenSatisfy(f: FV<V,E>): boolean { let rv: boolean = true; if (this.children!=null) { this.children.forEach( (v:Node<V,E>, e:E) => { if (!f(v)) rv = false; }); return rv; } else throw new Error('bad choreography'); } getChildrenAdornments(): Array<any> { if (this.children!=null) { return Array.from(this.children.values()).map( (x)=>x.adornment); } else throw new Error('bad choreography'); } adorn(o: any): any { const prevAdorn = this.adornment; this.adornment = o; return prevAdorn; } set(edge: E, node: Node<V,E>): ?Node<V,E> { if (this.children === null) { this.children = new Map(); } const children: ?Map<E, Node<V, E>> = this.children; if (children!=null) { const prevValue: ?Node<V, E> = children.get(edge); children.set(edge, node); return prevValue; } else throw new Error('bug1'); } setn(edge: E, node: Node<V,E>): void { const prevValue: ?Node<V, E> = this.set(edge, node); assert(prevValue===undefined); } isLeaf(): boolean { return this.children === null; } depthFirstTraversal(f: F<V,E>, _visitStartNode: ?boolean): void { const visitStartNode: boolean = _visitStartNode == null ? true : _visitStartNode; const cycleDetector: Array<Node<V,E>> = []; function _visit(n: Node<V,E>, parentN: ?Node<V,E>, birthEdge: ?E, firstVisit: boolean) { if (cycleDetector.includes(n)) throw new Error('cycle detected'); cycleDetector.push(n); if (!(firstVisit && !visitStartNode)) { f(n, parentN, birthEdge); } const children: ?Map<E, Node<V,E>> = n.children; if (children != null) { children.forEach( (v: Node<V,E>, k: E) => { _visit(v, n, k, false); }); } } _visit(this, null, null, true); } descendants(_includingThisNode: ?boolean): Array<Node<V, E>> { const includingThisNode: boolean = _includingThisNode == null ? false : _includingThisNode; const descendants: Array<Node<V, E>> = []; function f(n : Node<V, E>) { descendants.push(n); } this.depthFirstTraversal(f, includingThisNode); assert( ((!includingThisNode) && (descendants.length===0) && ( this.isLeaf())) || ((!includingThisNode) && (descendants.length> 0) && (!this.isLeaf())) || (( includingThisNode) && (descendants.length> 0)) ); return descendants; } leafs(_includingThisNode: ?boolean): Array<Node<V,E>> { const includingThisNode: boolean = _includingThisNode == null ? false : _includingThisNode; const rv: Array<Node<V,E>> = []; function addLeavesOnly(n: Node<V, E>): void { if (n.isLeaf()) rv.push(n); } this.depthFirstTraversal(addLeavesOnly, includingThisNode); return rv; } edgeThatLeadsTo(n: Node<V, E>): ?E { assert(!this.isLeaf()); const children: ?Map<E, Node<V, E>> = this.children; assert(children !== undefined); if (children != null) { const rv: Array<E> = []; children.forEach( function (child: Node<V, E>, edge: E) { const descendants: Array<Node<V, E>> = child.descendants(true); if (descendants.includes(n)) rv.push(edge); }); if (rv.length > 1) throw new Error('Bug! ${rv.length} edges leading to node: ${n} - impossible if the graph is a tree.'); else if (rv.length === 0) return null; else return rv[0]; } else throw new Error('bug3'); } print(printAdornment: boolean, _valuePrinter: ?ValuePrinter<V>, _adornPrinter: ?ValuePrinter<any>): string { let valuePrinter: ValuePrinter<V> = (_valuePrinter==null?(x:V)=>`${String(x)}`:_valuePrinter); let adornPrinter: ValuePrinter<any> = (_adornPrinter==null?(x)=>`${x}`:_adornPrinter); const s: symbol = Symbol.for(TREE_NODE_ID_SYMBOL_KEY);// Symbol(); let i: number = 0; const lines: Array<string> = []; const printerVisitor: F<V,E> = function printNode(n: Node<V,E>, parentN: ?Node<V,E>, birthEdge: ?E) { assert( ((parentN==null) && (birthEdge==null)) || ((parentN!=null) && (birthEdge!=null)) ); if (!n.hasOwnProperty(s)) // $SuppressFlowFinding: access of computed property/element. Indexable signature not found in ... n[s] = i++; if (parentN==null) { assert(parentN===null); assert(birthEdge===null); assert(_.isEmpty(lines)); // $SuppressFlowFinding: access of computed property/element. Indexable signature not found in ... let line: string = `ROOT node #${n[s]} with value: ${valuePrinter(n.value)}`; if (printAdornment) line+=`, adornment: ${n.adornment}`; lines.push(line); } else { assert(birthEdge !== undefined); if (birthEdge!=null) { // $SuppressFlowFinding: access of computed property/element. Indexable signature not found in ... let line: string = `node #${parentN[s]} ~~[${birthEdge}]~~> node #${n[s]} with value: ${valuePrinter(n.value)}`; if (printAdornment) line+=`, adornment: ${adornPrinter(n.adornment)}`; lines.push(line); } else throw new Error('bug'); } }; this.depthFirstTraversal(printerVisitor); return lines.join('\n'); } } exports.Node = Node; exports.TREE_NODE_ID_SYMBOL_KEY = TREE_NODE_ID_SYMBOL_KEY;