diff --git a/boost-app/src/features/play/gameTreesSlice.js b/boost-app/src/features/play/gameTreesSlice.js index cccd4dac3dc21b22fb65238c8e2aa0afaa0f2094..1fe05e23fc05ab4bc838a2647d80a01e3626ddc8 100644 --- a/boost-app/src/features/play/gameTreesSlice.js +++ b/boost-app/src/features/play/gameTreesSlice.js @@ -883,27 +883,30 @@ export const selectRerootSafeties = createObjectSelector( export const selectMoveTrees = createObjectSelector( [selectTrees], (tree) => { - function createSubtree(positionEncoding) { - const children = new Map(); - for (const move of Object.getOwnPropertyNames(positionEncoding.children)) { - const child = tree.positions[positionEncoding.children[move]]; - if (child !== undefined) { - children.set(move, createSubtree(child)); - } - } - return { + function createSubtree(positionEncoding, parent = undefined, priorAdvantage = undefined) { + const result = { identity: positionEncoding.identity, ply: positionEncoding.ply, - moves: [ - ...[positionEncoding.mainLineMove].filter( - (move) => move !== undefined, - ), - ...[...children.keys()].filter( - (move) => move !== positionEncoding.mainLineMove, - ).sort(), - ], - children, + advantage: positionEncoding.advantage !== undefined && priorAdvantage !== undefined ? + (positionEncoding.advantage + priorAdvantage) / 2 : + positionEncoding.advantage, + moves: [positionEncoding.mainLineMove].filter( + (move) => move !== undefined, + ), + suggestions: positionEncoding.suggestions, + parent, + children: new Map(), }; + for (const move of Object.getOwnPropertyNames(positionEncoding.children)) { + const childEncoding = tree.positions[positionEncoding.children[move]]; + if (childEncoding !== undefined) { + result.children.set(move, createSubtree(childEncoding, result, positionEncoding.advantage)); + } + } + result.moves.push(...[...result.children.keys()].filter( + (move) => move !== positionEncoding.mainLineMove, + ).sort()); + return result; } return createSubtree(tree.positions[tree.rootIdentity]); }, diff --git a/boost-app/src/features/play/gameTreesSlice.test.js b/boost-app/src/features/play/gameTreesSlice.test.js index 5ca26fd4d139987afa5f1a2277f0267519f78b48..4aaffe8a2da3d7563df193fd7edd40dbf656ee23 100644 --- a/boost-app/src/features/play/gameTreesSlice.test.js +++ b/boost-app/src/features/play/gameTreesSlice.test.js @@ -571,39 +571,59 @@ describe('in the game tree slice', () => { }, }; const result = selectMoveTrees({ [gameTreesSlice.name]: state }); + const oracle = { + identity: 0, + ply: 0, + advantage: 99, + moves: ['a', 'd'], + suggestions: ['y0y0', 'z0z0'], + parent: undefined, + children: new Map([ + ['a', { + identity: 1, + ply: 1, + advantage: 99, + moves: ['b', 'c'], + suggestions: ['y0y0', 'z0z0'], + parent: undefined, + children: new Map([ + ['b', { + identity: 2, + ply: 2, + advantage: 99, + moves: [], + suggestions: ['y0y0', 'z0z0'], + parent: undefined, + children: new Map(), + }], + ['c', { + identity: 3, + ply: 2, + advantage: 99, + moves: [], + suggestions: ['y0y0', 'z0z0'], + parent: undefined, + children: new Map(), + }], + ]), + }], + ['d', { + identity: 4, + ply: 1, + advantage: 99, + moves: [], + suggestions: ['y0y0', 'z0z0'], + parent: undefined, + children: new Map(), + }], + ]), + }; + oracle.children.get('a').parent = oracle; + oracle.children.get('a').children.get('b').parent = oracle.children.get('a'); + oracle.children.get('a').children.get('c').parent = oracle.children.get('a'); + oracle.children.get('d').parent = oracle; expect(result).toEqual({ - 'def': { - identity: 0, - ply: 0, - moves: ['a', 'd'], - children: new Map([ - ['a', { - identity: 1, - ply: 1, - moves: ['b', 'c'], - children: new Map([ - ['b', { - identity: 2, - ply: 2, - moves: [], - children: new Map(), - }], - ['c', { - identity: 3, - ply: 2, - moves: [], - children: new Map(), - }], - ]), - }], - ['d', { - identity: 4, - ply: 1, - moves: [], - children: new Map(), - }], - ]), - }, + 'def': oracle, }); }); test('lists main-line moves first', () => { @@ -638,39 +658,59 @@ describe('in the game tree slice', () => { }, }; const result = selectMoveTrees({ [gameTreesSlice.name]: state }); + const oracle = { + identity: 0, + ply: 0, + advantage: 99, + moves: ['d', 'a'], + suggestions: ['y0y0', 'z0z0'], + parent: undefined, + children: new Map([ + ['a', { + identity: 1, + ply: 1, + advantage: 99, + moves: ['c', 'b'], + suggestions: ['y0y0', 'z0z0'], + parent: undefined, + children: new Map([ + ['b', { + identity: 2, + ply: 2, + advantage: 99, + moves: [], + suggestions: ['y0y0', 'z0z0'], + parent: undefined, + children: new Map(), + }], + ['c', { + identity: 3, + ply: 2, + advantage: 99, + moves: [], + suggestions: ['y0y0', 'z0z0'], + parent: undefined, + children: new Map(), + }], + ]), + }], + ['d', { + identity: 4, + ply: 1, + advantage: 99, + moves: [], + suggestions: ['y0y0', 'z0z0'], + parent: undefined, + children: new Map(), + }], + ]), + }; + oracle.children.get('a').parent = oracle; + oracle.children.get('a').children.get('b').parent = oracle.children.get('a'); + oracle.children.get('a').children.get('c').parent = oracle.children.get('a'); + oracle.children.get('d').parent = oracle; expect(result).toEqual({ - 'def': { - identity: 0, - ply: 0, - moves: ['d', 'a'], - children: new Map([ - ['a', { - identity: 1, - ply: 1, - moves: ['c', 'b'], - children: new Map([ - ['b', { - identity: 2, - ply: 2, - moves: [], - children: new Map(), - }], - ['c', { - identity: 3, - ply: 2, - moves: [], - children: new Map(), - }], - ]), - }], - ['d', { - identity: 4, - ply: 1, - moves: [], - children: new Map(), - }], - ]), - }, + 'def': oracle, }); }); test('imitates a pure function despite returning an object literal and using `new`', () => { diff --git a/boost-app/src/features/play/scoreSheet.js b/boost-app/src/features/play/scoreSheet.js index 67ff9a643d0ff1783119e29aa5efe7a82c074bf2..6fe316c2918f83ef7c2fe83eb7c17dee15a6796c 100644 --- a/boost-app/src/features/play/scoreSheet.js +++ b/boost-app/src/features/play/scoreSheet.js @@ -21,6 +21,16 @@ import { setPosition, } from './gameTreesSlice.js'; +// eslint-disable-next-line no-unused-vars -- remove once this function is implemented and used +function getProgress(vertex) { + return undefined; +} + +// eslint-disable-next-line no-unused-vars -- remove once this function is implemented and used +function getAnnotation(vertex) { + return undefined; +} + function Move(props) { const button = useRef(); useEffect(() => {