diff --git a/trigo-web/.claude/agents/agentlog-updater.md b/trigo-web/.claude/agents/agentlog-updater.md new file mode 100644 index 0000000000000000000000000000000000000000..007d6c371176f90c1a3b538af8f114c8cd258ae2 --- /dev/null +++ b/trigo-web/.claude/agents/agentlog-updater.md @@ -0,0 +1,77 @@ +--- +name: agentlog-updater +description: Use this agent when a mini-milestone has been accomplished in the Trigo project and the development history needs to be documented in agentlog.md. This includes after completing features, fixing significant bugs, making architectural changes, or finishing logical chunks of work. Examples:\n\n\nContext: User has just finished implementing a new feature for multiplayer room management.\nuser: "I've finished adding the room creation and joining functionality"\nassistant: "Let me document this milestone in agentlog.md using the agentlog-updater agent."\n\nSince a development milestone has been reached, use the agentlog-updater agent to properly document it in the project's development history.\n\n\n\n\nContext: User mentions completing work on 3D board rendering improvements.\nuser: "完成了3D棋盘渲染的优化" (Chinese: "Completed optimization of 3D board rendering")\nassistant: "I'll use the agentlog-updater agent to document this milestone in agentlog.md with proper English translation and formatting."\n\nA milestone has been reached and needs documentation. The agent will handle translation and proper formatting according to project standards.\n\n\n\n\nContext: After a series of bug fixes and code refactoring.\nuser: "Can you update the agentlog with what we just did?"\nassistant: "I'll launch the agentlog-updater agent to review our recent conversation and document the mini-milestone in agentlog.md."\n\nUser explicitly requests agentlog update, triggering the specialized agent for this task.\n\n +tools: Glob, Grep, Read, WebFetch, TodoWrite, WebSearch, BashOutput, Edit, Write, NotebookEdit +model: haiku +color: green +--- + +You are the Agentlog Documentation Specialist, an expert technical writer specialized in maintaining clear, concise development histories for software projects. Your singular focus is documenting mini-milestones in the Trigo project's agentlog.md file. + +## Your Core Responsibilities + +1. **Analyze Recent Conversations**: Review the conversation history to identify the completed mini-milestone, extracting key accomplishments, decisions made, and technical details. + +2. **Format User Requests Properly**: + - Start user requests with `> ` prefix + - Translate non-English text to English while preserving technical terms + - Fix any typos or grammar errors + - Keep the request concise but complete (1-2 sentences typically) + - Maintain the user's intent and technical accuracy + +3. **Create Structured Agent Responses**: + - Enclose all agent response content within `
` and `
` tags + - Add a `` tag with a concise, descriptive title (5-10 words) + - Within the details block, document: + * What was accomplished + * Key technical decisions or approaches taken + * Any significant challenges or learnings + * Files or components affected + - Use clear, professional technical writing + - Format code references with backticks + - Use bullet points for lists + - Keep paragraphs focused and scannable + +4. **Maintain Consistency**: + - Study the existing agentlog.md format before writing + - Match the tone, style, and level of detail of previous entries + - Use the same heading structure and formatting conventions + - Ensure chronological ordering + +5. **Write in English Always**: All documentation must be in English, regardless of the original conversation language. Translate accurately while preserving technical precision. + +## Quality Standards + +- **Conciseness**: Every word should add value. Remove redundancy. +- **Clarity**: Technical details should be understandable to future developers +- **Completeness**: Capture the essence of what was accomplished without overwhelming detail +- **Accuracy**: Preserve technical terms, file paths, and technical decisions exactly +- **Professionalism**: Use formal technical writing style appropriate for project documentation + +## Your Workflow + +1. Read the entire recent conversation to understand the milestone +2. Review existing agentlog.md entries to match style and format +3. Extract the user's core request, translating and cleaning as needed +4. Synthesize the agent's work into a well-structured details block +5. Present the formatted entry for review before committing +6. Update agentlog.md with the new entry in chronological order + +## Format Template + +```markdown +> [User's request in English, concise, typos fixed] + +
+[Concise title of what was accomplished] + +[Well-structured documentation of the work, including: +- What was done +- Key technical details +- Files/components affected +- Any important decisions or learnings] + +
+``` + +You are meticulous about formatting, translation accuracy, and maintaining documentation consistency. Every entry you create should be a valuable reference for understanding the project's evolution. diff --git a/trigo-web/.env b/trigo-web/.env new file mode 100644 index 0000000000000000000000000000000000000000..8edbfdf78440fe7607e9c6f137fb57d37e9783eb --- /dev/null +++ b/trigo-web/.env @@ -0,0 +1,76 @@ +# Unified Environment Configuration for Trigo Web +# This file is used by frontend (Vite), backend (Express), and tools (Node scripts) +# +# LOCAL OVERRIDES: +# Create .env.local (not committed to git) to override any values below +# Example: cp .env.local.example .env.local +# +# Loading order: .env → .env.local (overrides) +# +# See .env.local.example for common override scenarios + +# ============================================================================ +# Frontend Configuration (Vite - requires VITE_ prefix) +# ============================================================================ + +# Backend Server URL +VITE_SERVER_URL=http://localhost:8157 + +# Vite Dev Server Configuration +VITE_HOST=0.0.0.0 +VITE_PORT=5173 + +# ONNX Model Paths (relative to /public directory) +# Evaluation mode model - predicts position value +VITE_ONNX_EVALUATION_MODEL=/onnx/20251230-trigo-value-llama-l6-h64-it2_251221-value0.01-pretrain/LlamaCausalLM_ep0036_evaluation.onnx + +# Tree mode model - generates move trees +VITE_ONNX_TREE_MODEL=/onnx/20251230-trigo-value-llama-l6-h64-it2_251221-value0.01-pretrain/LlamaCausalLM_ep0036_tree.onnx + + +# ============================================================================ +# Backend Configuration (Express Server) +# ============================================================================ + +# Server port (HTTP and Socket.io) +PORT=3000 + +# Frontend URL (used for CORS) +CLIENT_URL=http://localhost:5173 + +# Environment mode +NODE_ENV=development + + +# ============================================================================ +# Tools Configuration (Node scripts - tools/ directory) +# ============================================================================ + +# ONNX Model Paths (relative to project root) +# Evaluation mode model - predicts position value +ONNX_EVALUATION_MODEL=./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_evaluation.onnx + +# Tree mode model - generates move trees +ONNX_TREE_MODEL=./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_tree.onnx + +# ONNX Runtime Performance Configuration +# See docs/onnx-threading-configuration.md for detailed tuning guide + +# Intra-operator parallelism (threads within a single operator) +# Recommended: 4 for most systems, higher for large models +# ONNX_INTRA_OP_NUM_THREADS=4 +#ONNX_INTRA_OP_NUM_THREADS=28 + +# Inter-operator parallelism (threads across operators) +# Recommended: 1-2 for sequential models, higher for complex graphs +# ONNX_INTER_OP_NUM_THREADS=2 +ONNX_INTER_OP_NUM_THREADS=28 + +# Graph optimization level +# Options: "disabled", "basic", "extended", "all" (default: "all") +# ONNX_GRAPH_OPTIMIZATION_LEVEL=all + +# Memory optimization settings +# ONNX_ENABLE_CPU_MEM_ARENA=true +# ONNX_ENABLE_MEM_PATTERN=true + diff --git a/trigo-web/.env.local.example b/trigo-web/.env.local.example new file mode 100644 index 0000000000000000000000000000000000000000..6c5ca91711c25b5f77b31674377826d10750637a --- /dev/null +++ b/trigo-web/.env.local.example @@ -0,0 +1,43 @@ +# Local Environment Overrides +# +# This file is for local development overrides and is NOT committed to git. +# Copy this file to .env.local and customize as needed. +# +# Usage: +# cp .env.local.example .env.local +# # Edit .env.local with your local settings +# +# Any variables set here will override the values in .env + + +# ============================================================================== +# Example: Override ONNX Threading Configuration +# ============================================================================== + +# Uncomment and adjust for your CPU: +# ONNX_INTRA_OP_NUM_THREADS=8 +# ONNX_INTER_OP_NUM_THREADS=4 + + +# ============================================================================== +# Example: Use Different Model Versions for Testing +# ============================================================================== + +# ONNX_EVALUATION_MODEL=./public/onnx/experimental/model_v2_evaluation.onnx +# ONNX_TREE_MODEL=./public/onnx/experimental/model_v2_tree.onnx + + +# ============================================================================== +# Example: Frontend Development Settings +# ============================================================================== + +# VITE_PORT=3000 +# VITE_SERVER_URL=http://192.168.1.100:3000 + + +# ============================================================================== +# Example: Backend Development Settings +# ============================================================================== + +# PORT=4000 +# CLIENT_URL=http://localhost:3000 diff --git a/trigo-web/app/.env b/trigo-web/app/.env index 0c3534bf1d4197aebf30668fd9e897dc253084bc..0c088d8d8d27ba958cb0f35fbf3d074ccb2875f3 100644 --- a/trigo-web/app/.env +++ b/trigo-web/app/.env @@ -1,3 +1,8 @@ -VITE_SERVER_URL=http://localhost:3000 -VITE_HOST=0.0.0.0 -VITE_PORT=5173 \ No newline at end of file +# ============================================================================ +# Frontend Configuration +# ============================================================================ +# This file is DEPRECATED - all configuration is now in the root .env file +# Vite is configured to load from ../. env (repository root) +# +# To configure the frontend, edit: ../. env +# ============================================================================ diff --git a/trigo-web/app/package-lock.json b/trigo-web/app/package-lock.json index 0d4227cb320757fb75aec8046eec41f0a8203da2..43a85b7b923638bf7abf8509d4312014af97954d 100644 --- a/trigo-web/app/package-lock.json +++ b/trigo-web/app/package-lock.json @@ -8,8 +8,8 @@ "name": "trigo-app", "version": "0.0.0", "dependencies": { - "onnxruntime-common": "^1.23.2", - "onnxruntime-web": "^1.23.2", + "d3": "^7.9.0", + "d3-scale-chromatic": "^3.1.0", "pinia": "^2.1.6", "socket.io-client": "^4.5.2", "three": "^0.156.1", @@ -17,6 +17,7 @@ "vue-router": "^4.2.4" }, "devDependencies": { + "@types/d3": "^7.4.3", "@types/three": "^0.156.0", "@vitejs/plugin-vue": "^5.2.4", "sass-embedded": "^1.93.2", @@ -742,60 +743,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.52.5", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", @@ -1087,16 +1034,278 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dev": true, + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "dev": true + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "dev": true + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "dev": true + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "dev": true, + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "dev": true + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "dev": true + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "dev": true + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "dev": true + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dev": true, + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "dev": true + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "dev": true + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "dev": true, + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "dev": true + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dev": true, + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "dev": true + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "dev": true + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "dev": true + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "dev": true + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "dev": true, + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "dev": true + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "dev": true + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "dev": true, + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "dev": true + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "dev": true + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "dev": true + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dev": true, + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "dev": true + }, "node_modules/@types/node": { "version": "24.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "dev": true, + "optional": true, + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -1356,11 +1565,389 @@ "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==", "dev": true }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -1383,6 +1970,14 @@ } } }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -1489,11 +2084,6 @@ "node": ">=8" } }, - "node_modules/flatbuffers": { - "version": "25.9.23", - "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-25.9.23.tgz", - "integrity": "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1508,11 +2098,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/guid-typescript": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", - "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==" - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1531,12 +2116,31 @@ "he": "bin/he" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/immutable": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", "dev": true }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1570,11 +2174,6 @@ "node": ">=0.12.0" } }, - "node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" - }, "node_modules/magic-string": { "version": "0.30.19", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", @@ -1653,24 +2252,6 @@ "dev": true, "optional": true }, - "node_modules/onnxruntime-common": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.23.2.tgz", - "integrity": "sha512-5LFsC9Dukzp2WV6kNHYLNzp8sT6V02IubLCbzw2Xd6X5GOlr65gAX6xiJwyi2URJol/s71gaQLC5F2C25AAR2w==" - }, - "node_modules/onnxruntime-web": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.23.2.tgz", - "integrity": "sha512-T09JUtMn+CZLk3mFwqiH0lgQf+4S7+oYHHtk6uhaYAAJI95bTcKi5bOOZYwORXfS/RLZCjDDEXGWIuOCAFlEjg==", - "dependencies": { - "flatbuffers": "^25.1.24", - "guid-typescript": "^1.0.9", - "long": "^5.2.3", - "onnxruntime-common": "1.23.2", - "platform": "^1.3.6", - "protobufjs": "^7.2.4" - } - }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -1716,11 +2297,6 @@ } } }, - "node_modules/platform": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" - }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -1748,29 +2324,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/protobufjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", - "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -1785,6 +2338,11 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, "node_modules/rollup": { "version": "4.52.5", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", @@ -1826,6 +2384,11 @@ "fsevents": "~2.3.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -1835,6 +2398,11 @@ "tslib": "^2.1.0" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/sass": { "version": "1.93.2", "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.2.tgz", @@ -2296,7 +2864,10 @@ "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "optional": true, + "peer": true }, "node_modules/varint": { "version": "6.0.0", diff --git a/trigo-web/app/package.json b/trigo-web/app/package.json index 9ce91e271708ebba643f9f37f65e3949c5bdb77c..15ef470fd2f42e7e45035e41ae467cf138a53538 100644 --- a/trigo-web/app/package.json +++ b/trigo-web/app/package.json @@ -10,6 +10,8 @@ "preview": "vite preview" }, "dependencies": { + "d3": "^7.9.0", + "d3-scale-chromatic": "^3.1.0", "pinia": "^2.1.6", "socket.io-client": "^4.5.2", "three": "^0.156.1", @@ -18,6 +20,7 @@ "onnxruntime-web": "^1.23.2" }, "devDependencies": { + "@types/d3": "^7.4.3", "@types/three": "^0.156.0", "@vitejs/plugin-vue": "^5.2.4", "sass-embedded": "^1.93.2", diff --git a/trigo-web/app/src/components/InlineNicknameEditor.vue b/trigo-web/app/src/components/InlineNicknameEditor.vue new file mode 100644 index 0000000000000000000000000000000000000000..207aa7caae29daf9ca6a5627c68bf24d433c302a --- /dev/null +++ b/trigo-web/app/src/components/InlineNicknameEditor.vue @@ -0,0 +1,225 @@ + + + + + diff --git a/trigo-web/app/src/components/mcts/MCTSBoardHeatmap.vue b/trigo-web/app/src/components/mcts/MCTSBoardHeatmap.vue new file mode 100644 index 0000000000000000000000000000000000000000..eb163611902583397bd53ef0250c0c89a209590f --- /dev/null +++ b/trigo-web/app/src/components/mcts/MCTSBoardHeatmap.vue @@ -0,0 +1,434 @@ + + + + + diff --git a/trigo-web/app/src/components/mcts/MCTSDataLoader.vue b/trigo-web/app/src/components/mcts/MCTSDataLoader.vue new file mode 100644 index 0000000000000000000000000000000000000000..c3b320a5591849c5fbba57d3fca3a7a08800f7a7 --- /dev/null +++ b/trigo-web/app/src/components/mcts/MCTSDataLoader.vue @@ -0,0 +1,283 @@ + + + + + diff --git a/trigo-web/app/src/components/mcts/MCTSMoveNavigation.vue b/trigo-web/app/src/components/mcts/MCTSMoveNavigation.vue new file mode 100644 index 0000000000000000000000000000000000000000..1a724f67609f09055bf771879dccd49aea5110de --- /dev/null +++ b/trigo-web/app/src/components/mcts/MCTSMoveNavigation.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/trigo-web/app/src/components/mcts/MCTSStatisticsPanel.vue b/trigo-web/app/src/components/mcts/MCTSStatisticsPanel.vue new file mode 100644 index 0000000000000000000000000000000000000000..b50e8554f56b2ca9dca4fcca57f4bcca48e9808f --- /dev/null +++ b/trigo-web/app/src/components/mcts/MCTSStatisticsPanel.vue @@ -0,0 +1,221 @@ + + + + + diff --git a/trigo-web/app/src/components/mcts/MCTSTreeVisualization.vue b/trigo-web/app/src/components/mcts/MCTSTreeVisualization.vue new file mode 100644 index 0000000000000000000000000000000000000000..51933194e4f00e50e3da7662310f08350be77983 --- /dev/null +++ b/trigo-web/app/src/components/mcts/MCTSTreeVisualization.vue @@ -0,0 +1,407 @@ + + + + + diff --git a/trigo-web/app/src/composables/useRoomHash.ts b/trigo-web/app/src/composables/useRoomHash.ts new file mode 100644 index 0000000000000000000000000000000000000000..3a5514e87d6840523e036981fe9af64b3adfa14e --- /dev/null +++ b/trigo-web/app/src/composables/useRoomHash.ts @@ -0,0 +1,55 @@ +/** + * Composable for managing room ID in URL hash + * Enables shareable room links for VS People multiplayer mode + */ + +export function useRoomHash() { + /** + * Extract room ID from URL hash + * @returns Room ID string or null if no hash present + */ + function getRoomIdFromHash(): string | null { + const hash = window.location.hash.substring(1); // Remove '#' prefix + return hash || null; + } + + + /** + * Update URL hash with room ID + * Uses replaceState to avoid adding browser history entry + * @param roomId - 8-character uppercase alphanumeric room ID + */ + function updateHash(roomId: string): void { + const newUrl = `${window.location.pathname}#${roomId}`; + window.history.replaceState(null, "", newUrl); + } + + + /** + * Remove hash from URL + * Clears room ID from address bar + */ + function clearHash(): void { + const newUrl = window.location.pathname; + window.history.replaceState(null, "", newUrl); + } + + + /** + * Validate room ID format + * Must match backend generation: 8 uppercase alphanumeric characters + * @param roomId - Room ID to validate + * @returns true if valid format, false otherwise + */ + function isValidRoomId(roomId: string): boolean { + return /^[A-Z0-9]{8}$/.test(roomId); + } + + + return { + getRoomIdFromHash, + updateHash, + clearHash, + isValidRoomId + }; +} diff --git a/trigo-web/app/src/composables/useSocket.ts b/trigo-web/app/src/composables/useSocket.ts index b6a16032499141ee15ce25349bf21ee24739f04a..cd53d68f84782062dc5db5dfd03978124e5496f7 100644 --- a/trigo-web/app/src/composables/useSocket.ts +++ b/trigo-web/app/src/composables/useSocket.ts @@ -4,9 +4,11 @@ import { io, Socket } from "socket.io-client"; // Singleton socket instance let socketInstance: Socket | null = null; +// Singleton reactive refs (shared across all useSocket() calls) +const connected = ref(false); +const error = ref(null); + export function useSocket() { - const connected = ref(false); - const error = ref(null); // Get or create socket instance const getSocket = (): Socket => { @@ -16,10 +18,8 @@ export function useSocket() { let serverUrl: string; if (isDev) { - // Development: Use same host as frontend, port 3000 - // This allows accessing from local IP (e.g., 192.168.x.x:5173) - const currentHost = window.location.hostname; - serverUrl = `http://${currentHost}:3000`; + // Development: Use VITE_SERVER_URL from .env or .env.local + serverUrl = import.meta.env.VITE_SERVER_URL || "http://localhost:3000"; } else { // Production: same origin serverUrl = window.location.origin; @@ -50,6 +50,9 @@ export function useSocket() { error.value = err.message; console.error("[Socket.io] Connection error:", err.message); }); + } else { + // Socket already exists, sync the connected state + connected.value = socketInstance.connected; } return socketInstance; @@ -85,6 +88,171 @@ export function useSocket() { }); }; + // Join a room + const joinRoom = ( + data: { roomId?: string; nickname: string }, + callback: (response: any) => void + ): void => { + const socket = getSocket(); + console.log("[useSocket] joinRoom called:", { + roomId: data.roomId, + nickname: data.nickname, + socketConnected: socket.connected, + socketId: socket.id + }); + socket.emit("joinRoom", data, callback); + console.log("[useSocket] joinRoom event emitted"); + }; + + // Leave current room + const leaveRoom = (): void => { + const socket = getSocket(); + socket.emit("leaveRoom"); + }; + + // Change player nickname + const changeNickname = (nickname: string, callback?: (response: any) => void): void => { + const socket = getSocket(); + socket.emit("changeNickname", { nickname }, callback); + }; + + + // Game actions + + // Make a move (place stone) + const makeMove = (x: number, y: number, z: number): void => { + const socket = getSocket(); + socket.emit("makeMove", { x, y, z }); + }; + + // Pass turn + const pass = (): void => { + const socket = getSocket(); + socket.emit("pass"); + }; + + // Resign game + const resign = (): void => { + const socket = getSocket(); + socket.emit("resign"); + }; + + // Undo move + const undoMove = (callback?: (response: any) => void): void => { + const socket = getSocket(); + socket.emit("undoMove", callback); + }; + + // Redo move + const redoMove = (callback?: (response: any) => void): void => { + const socket = getSocket(); + socket.emit("redoMove", callback); + }; + + // Reset game + const resetGame = ( + options?: { boardShape?: { x: number; y: number; z: number }; playerColors?: { [playerId: string]: "black" | "white" } }, + callback?: (response: any) => void + ): void => { + const socket = getSocket(); + socket.emit("resetGame", options, callback); + }; + + + // Event listeners + const onPlayerJoined = (handler: (data: { playerId: string; nickname: string }) => void): void => { + const socket = getSocket(); + socket.on("playerJoined", handler); + }; + + const onPlayerLeft = (handler: (data: { playerId: string }) => void): void => { + const socket = getSocket(); + socket.on("playerLeft", handler); + }; + + const onNicknameChanged = ( + handler: (data: { playerId: string; nickname: string; oldNickname: string }) => void + ): void => { + const socket = getSocket(); + socket.on("nicknameChanged", handler); + }; + + const onRoomJoined = (handler: (data: any) => void): void => { + const socket = getSocket(); + socket.on("roomJoined", handler); + }; + + const onGameUpdate = (handler: (data: any) => void): void => { + const socket = getSocket(); + socket.on("gameUpdate", handler); + }; + + const onGameEnded = (handler: (data: any) => void): void => { + const socket = getSocket(); + socket.on("gameEnded", handler); + }; + + const onGameReset = (handler: (data: any) => void): void => { + const socket = getSocket(); + socket.on("gameReset", handler); + }; + + const onPlayerDisconnected = (handler: (data: { playerId: string }) => void): void => { + const socket = getSocket(); + socket.on("playerDisconnected", handler); + }; + + const onError = (handler: (data: { message: string }) => void): void => { + const socket = getSocket(); + socket.on("error", handler); + }; + + // Remove event listeners + const offPlayerJoined = (handler?: any): void => { + const socket = getSocket(); + socket.off("playerJoined", handler); + }; + + const offPlayerLeft = (handler?: any): void => { + const socket = getSocket(); + socket.off("playerLeft", handler); + }; + + const offNicknameChanged = (handler?: any): void => { + const socket = getSocket(); + socket.off("nicknameChanged", handler); + }; + + const offRoomJoined = (handler?: any): void => { + const socket = getSocket(); + socket.off("roomJoined", handler); + }; + + const offGameUpdate = (handler?: any): void => { + const socket = getSocket(); + socket.off("gameUpdate", handler); + }; + + const offGameEnded = (handler?: any): void => { + const socket = getSocket(); + socket.off("gameEnded", handler); + }; + + const offGameReset = (handler?: any): void => { + const socket = getSocket(); + socket.off("gameReset", handler); + }; + + const offPlayerDisconnected = (handler?: any): void => { + const socket = getSocket(); + socket.off("playerDisconnected", handler); + }; + + const offError = (handler?: any): void => { + const socket = getSocket(); + socket.off("error", handler); + }; + // Clean up on unmount onUnmounted(() => { // Note: We don't disconnect the singleton instance @@ -95,7 +263,38 @@ export function useSocket() { socket: getSocket(), connected, error, - sendEcho + sendEcho, + // Room management + joinRoom, + leaveRoom, + changeNickname, + // Game actions + makeMove, + pass, + resign, + undoMove, + redoMove, + resetGame, + // Event listeners + onPlayerJoined, + onPlayerLeft, + onNicknameChanged, + onRoomJoined, + onGameUpdate, + onGameEnded, + onGameReset, + onPlayerDisconnected, + onError, + // Event cleanup + offPlayerJoined, + offPlayerLeft, + offNicknameChanged, + offRoomJoined, + offGameUpdate, + offGameEnded, + offGameReset, + offPlayerDisconnected, + offError }; } diff --git a/trigo-web/app/src/composables/useTrigoAgent.ts b/trigo-web/app/src/composables/useTrigoAgent.ts index 36e0bdcab2cb13e4fc69cced4fe3003d6166a8b2..5b5b26c283fa799173c373e8a1d36e1e82729641 100644 --- a/trigo-web/app/src/composables/useTrigoAgent.ts +++ b/trigo-web/app/src/composables/useTrigoAgent.ts @@ -14,7 +14,7 @@ import { ref, onUnmounted } from "vue"; import { TrigoTreeAgent } from "@inc/trigoTreeAgent"; import { OnnxInferencer } from "@/services/onnxInferencer"; import type { TrigoGame } from "@inc/trigo/game"; -import type { Position } from "@inc/trigo/types"; +import type { Move } from "@inc/trigo/types"; /** * Composable for using the Trigo AI agent in Vue components @@ -49,9 +49,9 @@ export function useTrigoAgent() { // Create OnnxInferencer with evaluation model inferencer = new OnnxInferencer({ - modelPath: "/onnx/GPT2CausalLM_ep0015_evaluation.onnx", - vocabSize: 259, - seqLen: 2048 // Evaluation models support longer sequences + modelPath: import.meta.env.VITE_ONNX_TREE_MODEL, + vocabSize: 128, + seqLen: 256 }); // Initialize ONNX model @@ -75,10 +75,10 @@ export function useTrigoAgent() { * Generate the next move for the AI player * * @param game - Current game instance - * @returns The selected move position, or null if no valid moves or pass move + * @returns The selected move (can be a pass move with isPass: true), or null if no valid moves * @throws Error if agent is not initialized */ - const generateMove = async (game: TrigoGame): Promise => { + const generateMove = async (game: TrigoGame): Promise => { if (!agent) { throw new Error("AI agent not initialized. Call initialize() first."); } @@ -106,14 +106,19 @@ export function useTrigoAgent() { lastMoveTime.value = elapsed; console.log(`[useTrigoAgent] ✓ Move generated in ${elapsed.toFixed(2)}ms`); - // Convert Move to Position (return null if pass move) - if (!move || move.isPass) { - console.log("[useTrigoAgent] AI chose to pass"); + // Return the Move object directly (can be pass move or normal move) + if (!move) { + console.log("[useTrigoAgent] No valid move available"); return null; } + if (move.isPass) { + console.log("[useTrigoAgent] AI chose to pass"); + return move; // Return the pass move object + } + if (move.x !== undefined && move.y !== undefined && move.z !== undefined) { - return { x: move.x, y: move.y, z: move.z }; + return move; // Return the normal move object } console.warn("[useTrigoAgent] Move has undefined coordinates:", move); diff --git a/trigo-web/app/src/data/defaultNicknames.ts b/trigo-web/app/src/data/defaultNicknames.ts new file mode 100644 index 0000000000000000000000000000000000000000..342ac74f51f83d25db1bbeaed576a2c20bc0e1b0 --- /dev/null +++ b/trigo-web/app/src/data/defaultNicknames.ts @@ -0,0 +1,140 @@ +/** + * Default Nicknames for Trigo Players + * + * A curated list of common first names used for random nickname generation. + * When a user first visits the VS People mode, they're assigned a random + * nickname from this list. The nickname persists in localStorage and can + * be changed at any time. + */ + +export const DEFAULT_NICKNAMES: string[] = [ + // Male names (50) + "John", + "James", + "Michael", + "David", + "William", + "Richard", + "Joseph", + "Thomas", + "Charles", + "Daniel", + "Matthew", + "Anthony", + "Mark", + "Donald", + "Steven", + "Paul", + "Andrew", + "Joshua", + "Kenneth", + "Kevin", + "Brian", + "George", + "Edward", + "Ronald", + "Timothy", + "Jason", + "Jeffrey", + "Ryan", + "Jacob", + "Gary", + "Nicholas", + "Eric", + "Jonathan", + "Stephen", + "Larry", + "Justin", + "Scott", + "Brandon", + "Benjamin", + "Samuel", + "Raymond", + "Gregory", + "Frank", + "Alexander", + "Patrick", + "Jack", + "Dennis", + "Jerry", + "Tyler", + "Aaron", + + // Female names (50) + "Mary", + "Patricia", + "Jennifer", + "Linda", + "Barbara", + "Elizabeth", + "Susan", + "Jessica", + "Sarah", + "Karen", + "Nancy", + "Lisa", + "Betty", + "Margaret", + "Sandra", + "Ashley", + "Kimberly", + "Emily", + "Donna", + "Michelle", + "Dorothy", + "Carol", + "Amanda", + "Melissa", + "Deborah", + "Stephanie", + "Rebecca", + "Sharon", + "Laura", + "Cynthia", + "Kathleen", + "Amy", + "Angela", + "Shirley", + "Anna", + "Brenda", + "Pamela", + "Emma", + "Nicole", + "Helen", + "Samantha", + "Katherine", + "Christine", + "Debra", + "Rachel", + "Catherine", + "Carolyn", + "Janet", + "Ruth", + "Maria", + + // Neutral/Additional (15) + "Alex", + "Jordan", + "Taylor", + "Morgan", + "Casey", + "Riley", + "Jamie", + "Avery", + "Quinn", + "Sage", + "Robin", + "Charlie", + "Blake", + "Cameron", + "Hayden" +]; + +/** + * Get a random nickname from the default list + * @returns A randomly selected name from DEFAULT_NICKNAMES + */ +export function getRandomNickname(): string { + const index = Math.floor(Math.random() * DEFAULT_NICKNAMES.length); + return DEFAULT_NICKNAMES[index]; +} diff --git a/trigo-web/app/src/router/index.ts b/trigo-web/app/src/router/index.ts index 5652fd0edf5a0edb3abcbd479f9c2cd0f03f7e31..bfd73a8218235a115a863e1fa1c3e4ce9b35a26d 100644 --- a/trigo-web/app/src/router/index.ts +++ b/trigo-web/app/src/router/index.ts @@ -3,6 +3,8 @@ import TrigoView from "@/views/TrigoView.vue"; import OnnxTestView from "@/views/OnnxTestView.vue"; import TrigoAgentTestView from "@/views/TrigoAgentTestView.vue"; import TrigoTreeTestView from "@/views/TrigoTreeTestView.vue"; +import SocketTestView from "@/views/SocketTestView.vue"; +import MCTSAnalysisView from "@/views/MCTSAnalysisView.vue"; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), @@ -45,6 +47,16 @@ const router = createRouter({ path: "/tree-test", name: "tree-test", component: TrigoTreeTestView + }, + { + path: "/socket-test", + name: "socket-test", + component: SocketTestView + }, + { + path: "/mcts-analysis", + name: "mcts-analysis", + component: MCTSAnalysisView } ] }); diff --git a/trigo-web/app/src/stores/gameStore.ts b/trigo-web/app/src/stores/gameStore.ts index ee4d517f51bba6e39fe410bae003ec46183e9016..a697e3b83494fad95f9baa828999117336ee247a 100644 --- a/trigo-web/app/src/stores/gameStore.ts +++ b/trigo-web/app/src/stores/gameStore.ts @@ -297,6 +297,76 @@ export const useGameStore = defineStore("game", { } catch (error) { console.error("Failed to clear session storage:", error); } + }, + + + /** + * Set current player (for multiplayer sync) + */ + setCurrentPlayer(player: Player): void { + // This is handled by the underlying game state + // The current player is determined by the move history + // For proper sync, use loadFromTGN instead + console.log(`[gameStore] setCurrentPlayer called with: ${player}`); + }, + + + /** + * Load game state from TGN string (for multiplayer sync) + */ + loadFromTGN(tgn: string): boolean { + try { + // Create a new game from TGN + const { TrigoGame } = require("../../../inc/trigo/game"); + const newGame = TrigoGame.fromTGN(tgn); + + // Create new frontend game with same shape + const shape = newGame.getShape(); + this.game = new TrigoGameFrontend(shape); + + // Replay all moves to sync state + const history = newGame.getStepHistory(); + this.game.startGame(); + + for (const step of history) { + if (step.type === "drop" && step.position) { + this.game.makeMove(step.position.x, step.position.y, step.position.z); + } else if (step.type === "pass") { + this.game.pass(); + } + } + + this.saveToSessionStorage(); + return true; + } catch (error) { + console.error("[gameStore] Failed to load from TGN:", error); + return false; + } + }, + + + /** + * Get all stones on the board (for viewport sync) + */ + getAllStones(): Array<{ x: number; y: number; z: number; color: Player }> { + const stones: Array<{ x: number; y: number; z: number; color: Player }> = []; + const board = this.game.getBoard(); + const shape = this.game.getShape(); + + for (let x = 0; x < shape.x; x++) { + for (let y = 0; y < shape.y; y++) { + for (let z = 0; z < shape.z; z++) { + const stone = board[x][y][z]; + if (stone === 1) { + stones.push({ x, y, z, color: "black" }); + } else if (stone === 2) { + stones.push({ x, y, z, color: "white" }); + } + } + } + } + + return stones; } } }); diff --git a/trigo-web/app/src/stores/mctsStore.ts b/trigo-web/app/src/stores/mctsStore.ts new file mode 100644 index 0000000000000000000000000000000000000000..9692f13afc7582c9dbebef0af18dad0e8a67857a --- /dev/null +++ b/trigo-web/app/src/stores/mctsStore.ts @@ -0,0 +1,179 @@ +/** + * Pinia store for MCTS visualization state + */ + +import { defineStore } from "pinia"; +import { ref, computed } from "vue"; +import type { + MCTSMoveData, + MCTSMoveStatistic, + MCTSStatisticType, + ColorScaleType +} from "@/types/mcts"; +import type { Position } from "../../../inc/trigo/types"; + + +export const useMCTSStore = defineStore("mcts", () => { + // ============================================================================ + // State + // ============================================================================ + + // Game data + const gameHistory = ref([]); + const currentMoveIndex = ref(0); + + // Visualization settings + const selectedStatistic = ref("N"); + const colorScale = ref("linear"); + const topNFilter = ref(10); // Show top N moves in tree/table + + // Selection state + const selectedActionKey = ref(null); + + + // ============================================================================ + // Getters + // ============================================================================ + + const hasData = computed(() => gameHistory.value.length > 0); + + const currentMoveData = computed((): MCTSMoveData | null => { + if (!hasData.value) return null; + return gameHistory.value[currentMoveIndex.value] || null; + }); + + const maxMoveIndex = computed(() => { + return Math.max(0, gameHistory.value.length - 1); + }); + + const currentStatistics = computed((): MCTSMoveStatistic[] => { + return currentMoveData.value?.statistics || []; + }); + + const topMoves = computed((): MCTSMoveStatistic[] => { + const stats = currentStatistics.value; + return stats + .slice() + .sort((a, b) => b.N - a.N) + .slice(0, topNFilter.value); + }); + + const totalVisits = computed((): number => { + return currentStatistics.value.reduce((sum, stat) => sum + stat.N, 0); + }); + + const selectedMove = computed((): MCTSMoveStatistic | null => { + if (!selectedActionKey.value) return null; + return ( + currentStatistics.value.find( + (stat) => stat.actionKey === selectedActionKey.value + ) || null + ); + }); + + + // ============================================================================ + // Actions + // ============================================================================ + + function setGameHistory(history: MCTSMoveData[]) { + gameHistory.value = history; + currentMoveIndex.value = 0; + selectedActionKey.value = null; + } + + function clearGameHistory() { + gameHistory.value = []; + currentMoveIndex.value = 0; + selectedActionKey.value = null; + } + + function setCurrentMoveIndex(index: number) { + if (index < 0 || index > maxMoveIndex.value) return; + currentMoveIndex.value = index; + // Clear selection when moving to different move + selectedActionKey.value = null; + } + + function nextMove() { + if (currentMoveIndex.value < maxMoveIndex.value) { + setCurrentMoveIndex(currentMoveIndex.value + 1); + } + } + + function prevMove() { + if (currentMoveIndex.value > 0) { + setCurrentMoveIndex(currentMoveIndex.value - 1); + } + } + + function firstMove() { + setCurrentMoveIndex(0); + } + + function lastMove() { + setCurrentMoveIndex(maxMoveIndex.value); + } + + function setSelectedStatistic(stat: MCTSStatisticType) { + selectedStatistic.value = stat; + } + + function setColorScale(scale: ColorScaleType) { + colorScale.value = scale; + } + + function setTopNFilter(n: number) { + topNFilter.value = Math.max(3, Math.min(50, n)); + } + + function selectAction(actionKey: string | null) { + selectedActionKey.value = actionKey; + } + + function selectPosition(position: Position | null) { + if (!position) { + selectedActionKey.value = "pass"; + return; + } + selectedActionKey.value = `${position.x},${position.y},${position.z}`; + } + + + // ============================================================================ + // Return + // ============================================================================ + + return { + // State + gameHistory, + currentMoveIndex, + selectedStatistic, + colorScale, + topNFilter, + selectedActionKey, + + // Getters + hasData, + currentMoveData, + maxMoveIndex, + currentStatistics, + topMoves, + totalVisits, + selectedMove, + + // Actions + setGameHistory, + clearGameHistory, + setCurrentMoveIndex, + nextMove, + prevMove, + firstMove, + lastMove, + setSelectedStatistic, + setColorScale, + setTopNFilter, + selectAction, + selectPosition + }; +}); diff --git a/trigo-web/app/src/stores/playerStore.ts b/trigo-web/app/src/stores/playerStore.ts new file mode 100644 index 0000000000000000000000000000000000000000..c33bb384c5e4973cb98b8feecd8f2ed2806e1a95 --- /dev/null +++ b/trigo-web/app/src/stores/playerStore.ts @@ -0,0 +1,211 @@ +/** + * Player Store + * + * Manages player state for multiplayer mode, including nickname, connection status, + * and opponent information. + */ + +import { defineStore } from "pinia"; +import { StorageKey, localStorageManager } from "@/utils/storage"; +import { getRandomNickname } from "@/data/defaultNicknames"; + + +/** + * Player state interface + */ +export interface PlayerState { + nickname: string; // Local player nickname + playerId: string | null; // Socket ID + playerColor: "black" | "white" | null; // Player's stone color in current game + roomId: string | null; // Current room ID + opponentNickname: string | null; // Opponent's nickname + connectionStatus: "disconnected" | "connected" | "in-room"; // Connection state +} + + +/** + * Nickname validation result + */ +interface ValidationResult { + valid: boolean; + error?: string; +} + + +/** + * Validate a nickname against the rules: + * - Length: 3-20 characters + * - Characters: alphanumeric + spaces only + * - No leading/trailing whitespace + */ +function validateNickname(nickname: string): ValidationResult { + if (!nickname || typeof nickname !== "string") { + return { valid: false, error: "Nickname is required" }; + } + + if (nickname.length < 3) { + return { valid: false, error: "Nickname must be at least 3 characters" }; + } + + if (nickname.length > 20) { + return { valid: false, error: "Nickname must be 20 characters or less" }; + } + + if (!/^[a-zA-Z0-9 ]+$/.test(nickname)) { + return { valid: false, error: "Only letters, numbers, and spaces allowed" }; + } + + if (nickname.trim() !== nickname) { + return { valid: false, error: "No leading or trailing spaces allowed" }; + } + + return { valid: true }; +} + + +/** + * Load nickname from localStorage or generate a random one + */ +function loadNickname(): string { + const stored = localStorageManager.getString(StorageKey.PLAYER_NICKNAME); + if (stored) { + // Validate stored nickname + const validation = validateNickname(stored); + if (validation.valid) { + return stored; + } + } + // Generate random nickname if none exists or invalid + return getRandomNickname(); +} + + +/** + * Save nickname to localStorage + */ +function saveNickname(nickname: string): void { + localStorageManager.setString(StorageKey.PLAYER_NICKNAME, nickname); +} + + +/** + * Player store + */ +export const usePlayerStore = defineStore("player", { + state: (): PlayerState => ({ + nickname: loadNickname(), + playerId: null, + playerColor: null, + roomId: null, + opponentNickname: null, + connectionStatus: "disconnected" + }), + + getters: { + /** + * Is player currently in a room? + */ + isInRoom: (state): boolean => state.connectionStatus === "in-room", + + /** + * Does player have an opponent in the room? + */ + hasOpponent: (state): boolean => state.opponentNickname !== null, + + /** + * Display name with fallback to "Guest" + */ + displayName: (state): string => state.nickname || "Guest", + + /** + * Is player connected to socket server? + */ + isConnected: (state): boolean => state.connectionStatus !== "disconnected" + }, + + actions: { + /** + * Set the player's nickname + * @param nickname - New nickname to set + * @returns true if successful, false if validation failed + */ + setNickname(nickname: string): boolean { + const validation = validateNickname(nickname); + if (!validation.valid) { + console.warn(`[PlayerStore] Invalid nickname: ${validation.error}`); + return false; + } + + this.nickname = nickname; + saveNickname(nickname); + return true; + }, + + /** + * Set the player's socket ID + * @param id - Socket ID from server + */ + setPlayerId(id: string): void { + this.playerId = id; + this.connectionStatus = "connected"; + }, + + /** + * Join a room with the given ID and assigned color + * @param roomId - Room ID + * @param color - Assigned player color + */ + joinRoom(roomId: string, color: "black" | "white"): void { + this.roomId = roomId; + this.playerColor = color; + this.connectionStatus = "in-room"; + }, + + /** + * Set the opponent's nickname + * @param nickname - Opponent's nickname + */ + setOpponentNickname(nickname: string): void { + this.opponentNickname = nickname; + }, + + /** + * Leave the current room + */ + leaveRoom(): void { + this.roomId = null; + this.playerColor = null; + this.opponentNickname = null; + this.connectionStatus = "connected"; + }, + + /** + * Disconnect from the server + */ + disconnect(): void { + this.playerId = null; + this.roomId = null; + this.playerColor = null; + this.opponentNickname = null; + this.connectionStatus = "disconnected"; + }, + + /** + * Reset player state to initial state + * Note: Nickname is preserved in localStorage + */ + reset(): void { + this.playerId = null; + this.playerColor = null; + this.roomId = null; + this.opponentNickname = null; + this.connectionStatus = "disconnected"; + } + } +}); + + +/** + * Export validation function for use in other components + */ +export { validateNickname }; diff --git a/trigo-web/app/src/styles/test-pages.scss b/trigo-web/app/src/styles/test-pages.scss new file mode 100644 index 0000000000000000000000000000000000000000..e8b1b48668bf887d11184706532f9e27c5260c1c --- /dev/null +++ b/trigo-web/app/src/styles/test-pages.scss @@ -0,0 +1,554 @@ +/** + * Shared styles for test/analysis pages + * + * Provides consistent styling for: + * - MCTSAnalysisView + * - TrigoTreeTestView + * - TrigoAgentTestView + * - OnnxTestView + * - SocketTestView + */ + + +// ============================================================================ +// Theme Variables +// ============================================================================ + +// Light theme colors +$light-bg-primary: #f5f5f5; +$light-bg-secondary: white; +$light-bg-tertiary: #f8f9fa; +$light-text-primary: #333; +$light-text-secondary: #666; +$light-border: #e1e4e8; +$light-shadow: rgba(0, 0, 0, 0.1); + +// Dark theme colors +$dark-bg-primary: #1a1a1a; +$dark-bg-secondary: #2a2a2a; +$dark-bg-tertiary: #3a3a3a; +$dark-text-primary: #e0e0e0; +$dark-text-secondary: #a0a0a0; +$dark-border: #404040; +$dark-shadow: rgba(0, 0, 0, 0.3); + +// Status colors (shared) +$color-success: #28a745; +$color-warning: #ffc107; +$color-error: #dc3545; +$color-info: #007bff; +$color-primary: #4a90e2; +$color-accent: #e94560; + + +// ============================================================================ +// Custom Scrollbar Mixin +// ============================================================================ + +@mixin custom-scrollbar($theme: 'dark') { + @if $theme == 'dark' { + &::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + &::-webkit-scrollbar-track { + background: $dark-bg-primary; + border-radius: 4px; + } + + &::-webkit-scrollbar-thumb { + background: #4a4a4a; + border-radius: 4px; + + &:hover { + background: #5a5a5a; + } + } + } @else { + &::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + &::-webkit-scrollbar-track { + background: $light-bg-tertiary; + border-radius: 4px; + } + + &::-webkit-scrollbar-thumb { + background: #c0c0c0; + border-radius: 4px; + + &:hover { + background: #a0a0a0; + } + } + } +} + + +// ============================================================================ +// Base Container Styles +// ============================================================================ + +@mixin test-page-container($theme: 'light') { + width: 100%; + height: 100vh; + overflow-y: auto; + overflow-x: hidden; + + @if $theme == 'dark' { + background-color: $dark-bg-primary; + color: $dark-text-primary; + } @else { + background-color: $light-bg-primary; + color: $light-text-primary; + } + + @include custom-scrollbar($theme); +} + + +// ============================================================================ +// Header Styles +// ============================================================================ + +@mixin test-page-header() { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + padding: 2rem; + text-align: center; + + h1 { + margin: 0 0 0.5rem 0; + font-size: 2rem; + font-weight: 700; + } + + .subtitle { + margin: 0; + opacity: 0.9; + font-size: 1.1rem; + } +} + + +// ============================================================================ +// Section Styles +// ============================================================================ + +@mixin test-section($theme: 'light') { + border-radius: 8px; + padding: 20px; + margin-bottom: 20px; + + @if $theme == 'dark' { + background-color: $dark-bg-secondary; + border: 1px solid $dark-border; + } @else { + background-color: $light-bg-secondary; + border: 1px solid $light-border; + box-shadow: 0 2px 8px $light-shadow; + } + + h2, h3 { + margin-top: 0; + margin-bottom: 15px; + font-weight: 600; + + @if $theme == 'dark' { + color: $color-primary; + } @else { + color: $light-text-primary; + } + } + + h2 { + font-size: 20px; + } + + h3 { + font-size: 16px; + } + + p { + margin: 0 0 1rem 0; + + @if $theme == 'dark' { + color: $dark-text-secondary; + } @else { + color: $light-text-secondary; + } + } +} + + +// ============================================================================ +// Panel Styles (for sidebars/control panels) +// ============================================================================ + +@mixin control-panel($theme: 'dark') { + border-radius: 8px; + padding: 20px; + overflow-y: auto; + max-height: calc(100vh - 80px); + + @if $theme == 'dark' { + background-color: $dark-bg-secondary; + } @else { + background-color: $light-bg-secondary; + border: 1px solid $light-border; + } + + @include custom-scrollbar($theme); + + h2 { + margin: 0 0 20px 0; + font-size: 20px; + padding-bottom: 10px; + + @if $theme == 'dark' { + color: $color-accent; + border-bottom: 1px solid $dark-border; + } @else { + color: $light-text-primary; + border-bottom: 1px solid $light-border; + } + } + + h3 { + margin: 0 0 15px 0; + font-size: 16px; + color: $color-primary; + } +} + + +// ============================================================================ +// Button Styles +// ============================================================================ + +@mixin test-button($variant: 'primary') { + padding: 0.75rem 1.5rem; + border: none; + border-radius: 8px; + font-weight: 600; + font-size: 1rem; + cursor: pointer; + transition: all 0.3s ease; + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + @if $variant == 'primary' { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + + &:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); + } + } @else if $variant == 'secondary' { + background: #6c757d; + color: white; + + &:hover:not(:disabled) { + background: #5a6268; + } + } @else if $variant == 'success' { + background: $color-success; + color: white; + + &:hover:not(:disabled) { + background: darken($color-success, 10%); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba($color-success, 0.4); + } + } @else if $variant == 'test' { + background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); + color: white; + + &:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(245, 87, 108, 0.4); + } + } +} + + +// ============================================================================ +// Status Display +// ============================================================================ + +@mixin status-display($theme: 'light') { + border-radius: 8px; + padding: 1rem; + margin-bottom: 1rem; + + @if $theme == 'dark' { + background-color: $dark-bg-primary; + } @else { + background-color: $light-bg-tertiary; + } + + .status-item { + display: flex; + justify-content: space-between; + padding: 0.5rem 0; + + @if $theme == 'dark' { + border-bottom: 1px solid $dark-border; + } @else { + border-bottom: 1px solid #e9ecef; + } + + &:last-child { + border-bottom: none; + } + + .status-label { + font-weight: 600; + + @if $theme == 'dark' { + color: $dark-text-secondary; + } @else { + color: $light-text-secondary; + } + } + + .status-value { + @if $theme == 'dark' { + color: $dark-text-primary; + } @else { + color: $light-text-primary; + } + } + + &.ready .status-value { + color: $color-success; + font-weight: 600; + } + + &.loading .status-value { + color: $color-warning; + font-weight: 600; + } + + &.error .status-value { + color: $color-error; + font-weight: 600; + } + } +} + + +// ============================================================================ +// Error/Success Messages +// ============================================================================ + +@mixin error-message() { + background: #fff3cd; + border: 1px solid #ffeaa7; + border-radius: 6px; + padding: 0.75rem; + color: #856404; + margin-top: 1rem; +} + +@mixin success-message() { + background: #d4edda; + border: 1px solid #c3e6cb; + border-radius: 6px; + padding: 0.75rem; + color: #155724; + font-weight: 600; + margin-top: 1rem; +} + + +// ============================================================================ +// Table Styles +// ============================================================================ + +@mixin data-table($theme: 'light') { + width: 100%; + border-collapse: collapse; + font-size: 12px; + + thead { + position: sticky; + top: 0; + z-index: 1; + + @if $theme == 'dark' { + background-color: $dark-bg-secondary; + } @else { + background-color: $light-bg-tertiary; + } + + th { + padding: 8px 6px; + text-align: left; + font-weight: 600; + + @if $theme == 'dark' { + color: $color-primary; + border-bottom: 1px solid $dark-border; + } @else { + color: $light-text-secondary; + border-bottom: 2px solid #dee2e6; + } + } + } + + tbody { + tr { + cursor: pointer; + transition: background-color 0.2s; + + @if $theme == 'dark' { + &:hover { + background-color: $dark-bg-tertiary; + } + + &.selected { + background-color: #4a3030; + + td { + color: $color-accent; + font-weight: 500; + } + } + } @else { + &:hover { + background-color: $light-bg-tertiary; + } + + &.selected { + background-color: #e7f5ff; + + td { + color: $color-info; + font-weight: 500; + } + } + } + } + + td { + padding: 8px 6px; + + @if $theme == 'dark' { + color: #c0c0c0; + border-bottom: 1px solid #303030; + } @else { + color: $light-text-primary; + border-bottom: 1px solid #dee2e6; + } + } + } +} + + +// ============================================================================ +// Input Styles +// ============================================================================ + +@mixin input-field($theme: 'light') { + width: 100%; + padding: 0.75rem; + border-radius: 8px; + font-size: 1rem; + transition: border-color 0.3s ease; + + @if $theme == 'dark' { + background-color: $dark-bg-primary; + border: 2px solid $dark-border; + color: $dark-text-primary; + + &:focus { + outline: none; + border-color: $color-primary; + } + + &:disabled { + background-color: $dark-bg-tertiary; + cursor: not-allowed; + } + } @else { + background-color: white; + border: 2px solid #e9ecef; + color: $light-text-primary; + + &:focus { + outline: none; + border-color: #667eea; + } + + &:disabled { + background-color: $light-bg-tertiary; + cursor: not-allowed; + } + } +} + + +// ============================================================================ +// Code/Debug Display +// ============================================================================ + +@mixin code-display($theme: 'light') { + border-radius: 8px; + padding: 1rem; + font-family: "Courier New", monospace; + font-size: 0.9rem; + overflow-x: auto; + white-space: pre-wrap; + word-break: break-word; + margin: 0; + + @if $theme == 'dark' { + background-color: $dark-bg-primary; + color: $dark-text-primary; + border: 1px solid $dark-border; + } @else { + background-color: white; + color: $light-text-primary; + border: 1px solid $light-border; + } +} + + +// ============================================================================ +// Utility Classes +// ============================================================================ + +.no-data-message { + text-align: center; + padding: 2rem; + font-size: 14px; + font-style: italic; +} + +.panel-section { + margin-bottom: 30px; + + &:last-child { + margin-bottom: 0; + } +} + +.button-group { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; +} + +.info-text { + font-size: 13px; + line-height: 1.5; + margin: 0 0 15px 0; +} diff --git a/trigo-web/app/src/types/mcts.ts b/trigo-web/app/src/types/mcts.ts new file mode 100644 index 0000000000000000000000000000000000000000..339dc5f035a27d5b13ecebe9986b4b604341d97b --- /dev/null +++ b/trigo-web/app/src/types/mcts.ts @@ -0,0 +1,52 @@ +/** + * TypeScript type definitions for MCTS visualization + */ + +import type { TrigoGame } from "../../../inc/trigo/game"; +import type { Position } from "../../../inc/trigo/types"; + + +/** + * Statistics for a single move (position) at a given game state + */ +export interface MCTSMoveStatistic { + position: Position | null; // null for pass move + actionKey: string; // "x,y,z" or "pass" + N: number; // Visit count + pi: number; // Search policy (normalized visit count) +} + + +/** + * Complete MCTS data for a single move in the game + */ +export interface MCTSMoveData { + moveNumber: number; // Move index (0-based) + player: "black" | "white"; // Player to move + gameState: TrigoGame; // Game state at this move + statistics: MCTSMoveStatistic[]; // Statistics for all legal moves +} + + +/** + * Statistic type for visualization selection + */ +export type MCTSStatisticType = "N" | "pi"; + + +/** + * Color scale type for heatmap + */ +export type ColorScaleType = "linear" | "log"; + + +/** + * Tree node for D3 visualization + */ +export interface MCTSTreeNode { + id: string; + label: string; + N: number; + pi: number; + children: MCTSTreeNode[]; +} diff --git a/trigo-web/app/src/utils/mctsColorScale.ts b/trigo-web/app/src/utils/mctsColorScale.ts new file mode 100644 index 0000000000000000000000000000000000000000..9cfc4e43786bfaf1b37dad519652876fe06fc9ac --- /dev/null +++ b/trigo-web/app/src/utils/mctsColorScale.ts @@ -0,0 +1,184 @@ +/** + * Color scale utilities for MCTS visualization + * + * Uses D3 color scales to map statistics to colors + */ + +import * as d3 from "d3"; +import * as d3Chromatic from "d3-scale-chromatic"; +import type { MCTSStatisticType, ColorScaleType } from "@/types/mcts"; + + +/** + * Get color for a statistic value + * + * @param value - The statistic value + * @param statistic - Which statistic (N or pi) + * @param min - Minimum value in the range + * @param max - Maximum value in the range + * @param scale - Linear or logarithmic scale + * @returns CSS color string + */ +export function getColorForStatistic( + value: number, + statistic: MCTSStatisticType, + min: number, + max: number, + scale: ColorScaleType = "linear" +): string { + // Handle edge cases + if (!Number.isFinite(value) || max === min) { + return "#808080"; // Gray for undefined/invalid + } + + // Normalize value to [0, 1] + let normalized: number; + + if (scale === "log") { + // Logarithmic scale (useful for visit counts with wide range) + const logMin = Math.log(Math.max(min, 1)); + const logMax = Math.log(Math.max(max, 1)); + const logValue = Math.log(Math.max(value, 1)); + normalized = (logValue - logMin) / (logMax - logMin); + } else { + // Linear scale + normalized = (value - min) / (max - min); + } + + // Clamp to [0, 1] + normalized = Math.max(0, Math.min(1, normalized)); + + // Apply color scale based on statistic type + switch (statistic) { + case "N": + // Visit counts: Blue scale (light to dark) + return d3Chromatic.interpolateBlues(normalized * 0.9 + 0.1); + + case "pi": + // Search policy: Yellow-Orange-Red scale + return d3Chromatic.interpolateYlOrRd(normalized * 0.9 + 0.1); + + default: + return "#808080"; + } +} + + +/** + * Create a D3 color scale for a statistic + * + * @param statistic - Which statistic + * @param min - Minimum value + * @param max - Maximum value + * @param scaleType - Linear or logarithmic + * @returns D3 scale function + */ +export function createColorScale( + statistic: MCTSStatisticType, + min: number, + max: number, + scaleType: ColorScaleType = "linear" +) { + // Create domain based on scale type + let domain: number[]; + + if (scaleType === "log") { + const logMin = Math.log(Math.max(min, 1)); + const logMax = Math.log(Math.max(max, 1)); + domain = [logMin, logMax]; + } else { + domain = [min, max]; + } + + // Create range based on statistic + let range: string[]; + + switch (statistic) { + case "N": + // Blues + range = [d3Chromatic.interpolateBlues(0.1), d3Chromatic.interpolateBlues(0.9)]; + break; + + case "pi": + // Yellow-Orange-Red + range = [d3Chromatic.interpolateYlOrRd(0.1), d3Chromatic.interpolateYlOrRd(0.9)]; + break; + + default: + range = ["#e0e0e0", "#404040"]; + } + + // Create scale + if (scaleType === "log") { + return d3.scaleLinear().domain(domain).range(range); + } else { + return d3.scaleLinear().domain(domain).range(range); + } +} + + +/** + * Generate legend color stops for display + * + * @param statistic - Which statistic + * @param min - Minimum value + * @param max - Maximum value + * @param steps - Number of color stops (default: 10) + * @returns Array of {value, color} objects + */ +export function generateLegendStops( + statistic: MCTSStatisticType, + min: number, + max: number, + steps: number = 10 +): Array<{ value: number; color: string; label: string }> { + const stops: Array<{ value: number; color: string; label: string }> = []; + + for (let i = 0; i <= steps; i++) { + const t = i / steps; + const value = min + (max - min) * t; + const color = getColorForStatistic(value, statistic, min, max, "linear"); + + // Format label based on statistic type + let label: string; + if (statistic === "pi") { + label = `${(value * 100).toFixed(1)}%`; + } else { + label = value < 10 ? value.toFixed(1) : Math.round(value).toString(); + } + + stops.push({ value, color, label }); + } + + return stops; +} + + +/** + * Get appropriate label for a statistic + */ +export function getStatisticLabel(statistic: MCTSStatisticType): string { + switch (statistic) { + case "N": + return "Visit Count"; + case "pi": + return "Search Policy π"; + default: + return "Unknown"; + } +} + + +/** + * Get descriptive text for a statistic + */ +export function getStatisticDescription(statistic: MCTSStatisticType): string { + switch (statistic) { + case "N": + return "Number of times MCTS explored this move"; + case "pi": + return "Final probability of selecting this move after search"; + default: + return ""; + } +} diff --git a/trigo-web/app/src/utils/mctsDataParser.ts b/trigo-web/app/src/utils/mctsDataParser.ts new file mode 100644 index 0000000000000000000000000000000000000000..4d29894680975e036b561ff7879de5b7153f8e72 --- /dev/null +++ b/trigo-web/app/src/utils/mctsDataParser.ts @@ -0,0 +1,174 @@ +/** + * MCTS Data Parser + * + * Parses TGN game files and visit count JSON files to reconstruct + * move-by-move MCTS statistics. + */ + +import { TrigoGame } from "../../../inc/trigo/game"; +import type { MCTSMoveData, MCTSMoveStatistic } from "../types/mcts"; + + +/** + * Parse MCTS data from TGN and visit counts JSON + * + * @param tgnContent - TGN game notation string + * @param visitCountsJson - JSON string containing visit counts array + * @returns Array of MCTS move data for each move in the game + * @throws Error if parsing fails or data is invalid + */ +export async function parseMCTSData( + tgnContent: string, + visitCountsJson: string +): Promise { + // 1. Parse TGN to get game + let game: TrigoGame; + try { + game = TrigoGame.fromTGN(tgnContent); + } catch (error) { + throw new Error(`Failed to parse TGN file: ${error instanceof Error ? error.message : String(error)}`); + } + + // 2. Validate board shape (must be 2D) + const shape = game.getShape(); + if (shape.z !== 1) { + throw new Error( + `Only 2D boards (z=1) are supported. This game has shape ${shape.x}×${shape.y}×${shape.z}` + ); + } + + // 3. Parse visit counts JSON + let visitCounts: number[][]; + try { + visitCounts = JSON.parse(visitCountsJson); + + if (!Array.isArray(visitCounts)) { + throw new Error("Visit counts must be an array"); + } + + if (visitCounts.length === 0) { + throw new Error("Visit counts array is empty"); + } + + // Validate that each element is an array of numbers + for (let i = 0; i < visitCounts.length; i++) { + if (!Array.isArray(visitCounts[i])) { + throw new Error(`Visit counts at index ${i} is not an array`); + } + } + } catch (error) { + throw new Error(`Failed to parse visit counts JSON: ${error instanceof Error ? error.message : String(error)}`); + } + + // 4. Replay game move-by-move and map visit counts to positions + const result: MCTSMoveData[] = []; + const replayGame = new TrigoGame(shape, {}); + const stepHistory = game.getHistory(); + + // Ensure we have visit counts for each move + if (visitCounts.length !== stepHistory.length) { + console.warn( + `Visit counts length (${visitCounts.length}) doesn't match step history length (${stepHistory.length}). Using minimum.` + ); + } + + const minLength = Math.min(visitCounts.length, stepHistory.length); + + for (let i = 0; i < minLength; i++) { + // Get current player + const currentPlayer = replayGame.getCurrentPlayer() === 1 ? "black" : "white"; + + // Get all valid positions at this state + const validPos = replayGame.validMovePositions(); + + // Map visit counts to positions + const statistics: MCTSMoveStatistic[] = validPos.map((pos, idx) => ({ + position: pos, + actionKey: `${pos.x},${pos.y},${pos.z}`, + N: visitCounts[i][idx] || 0, + pi: 0 // Will be calculated after + })); + + // Add pass move (typically the last element in visit counts) + const passN = visitCounts[i][validPos.length] || 0; + statistics.push({ + position: null, + actionKey: "pass", + N: passN, + pi: 0 + }); + + // Calculate total visits + const totalN = statistics.reduce((sum, stat) => sum + stat.N, 0); + + // Normalize to get search policy π + if (totalN > 0) { + statistics.forEach((stat) => { + stat.pi = stat.N / totalN; + }); + } + + // Store move data + result.push({ + moveNumber: i, + player: currentPlayer, + gameState: replayGame.clone(), + statistics + }); + + // Apply move to advance game state + const step = stepHistory[i]; + if (step.type === 1) { // StepType.PASS + replayGame.pass(); + } else if (step.position) { + replayGame.drop(step.position); + } + } + + if (result.length === 0) { + throw new Error("No MCTS data could be parsed"); + } + + console.log(`[MCTS Parser] Successfully parsed ${result.length} moves`); + return result; +} + + +/** + * Validate that visit counts data has the expected structure + */ +export function validateVisitCounts(visitCounts: any): visitCounts is number[][] { + if (!Array.isArray(visitCounts)) { + return false; + } + + for (const counts of visitCounts) { + if (!Array.isArray(counts)) { + return false; + } + + for (const count of counts) { + if (typeof count !== "number" || !Number.isFinite(count) || count < 0) { + return false; + } + } + } + + return true; +} + + +/** + * Format position for display + */ +export function formatPosition(position: { x: number; y: number; z?: number } | null): string { + if (!position) { + return "Pass"; + } + + // Convert to chess-like notation: column letter + row number + const col = String.fromCharCode(65 + position.x); // A, B, C, ... + const row = position.y + 1; // 1, 2, 3, ... + + return `${col}${row}`; +} diff --git a/trigo-web/app/src/utils/storage.ts b/trigo-web/app/src/utils/storage.ts index d735d61150ca470a43aac47f8761269d088d21d7..13118dc7101fb1e45449e3148968f4dd052a2eab 100644 --- a/trigo-web/app/src/utils/storage.ts +++ b/trigo-web/app/src/utils/storage.ts @@ -32,6 +32,9 @@ export enum StorageKey { // AI settings AI_PLAYER_COLOR = "trigoAIPlayerColor", + // Player settings + PLAYER_NICKNAME = "trigoPlayerNickname", + // User preferences (examples for future use) // THEME = "trigoTheme", // LANGUAGE = "trigoLanguage", diff --git a/trigo-web/app/src/views/MCTSAnalysisView.vue b/trigo-web/app/src/views/MCTSAnalysisView.vue new file mode 100644 index 0000000000000000000000000000000000000000..912c0c338a08b0dd6aa0b58c0082e1a3149eaee1 --- /dev/null +++ b/trigo-web/app/src/views/MCTSAnalysisView.vue @@ -0,0 +1,176 @@ + + + + + diff --git a/trigo-web/app/src/views/OnnxTestView.vue b/trigo-web/app/src/views/OnnxTestView.vue index bd2efaa91e83f50d35c3822640edd1de694377f2..2aad0e22cf0bf8b1d97bbebbc45d6ac7246bb424 100644 --- a/trigo-web/app/src/views/OnnxTestView.vue +++ b/trigo-web/app/src/views/OnnxTestView.vue @@ -179,9 +179,9 @@ console.log("=".repeat(80)); inferencer = new OnnxInferencer({ - modelPath: "/onnx/GPT2CausalLM_ep0015_int8_seq2048_int8.onnx", - vocabSize: 259, - seqLen: 2048, + modelPath: import.meta.env.VITE_ONNX_TREE_MODEL, + vocabSize: 128, + seqLen: 256, executionProviders: ["wasm"] // Use WebAssembly for compatibility }); diff --git a/trigo-web/app/src/views/SocketTestView.vue b/trigo-web/app/src/views/SocketTestView.vue new file mode 100644 index 0000000000000000000000000000000000000000..927083e942a50b87636eb31355f2f64d45665d6e --- /dev/null +++ b/trigo-web/app/src/views/SocketTestView.vue @@ -0,0 +1,932 @@ + + + + + diff --git a/trigo-web/app/src/views/TrigoAgentTestView.vue b/trigo-web/app/src/views/TrigoAgentTestView.vue index f12996978fe82ad946754d535f3bf4c394c1ac04..8546e71300f49cf5a936716a854af0f70f701daf 100644 --- a/trigo-web/app/src/views/TrigoAgentTestView.vue +++ b/trigo-web/app/src/views/TrigoAgentTestView.vue @@ -218,9 +218,9 @@ // Create and initialize inferencer inferencer = new OnnxInferencer({ - modelPath: "/onnx/GPT2CausalLM_ep0015_int8_seq2048_int8.onnx", - vocabSize: 259, - seqLen: 2048 + modelPath: import.meta.env.VITE_ONNX_TREE_MODEL, + vocabSize: 128, + seqLen: 256 }); await inferencer.initialize(); diff --git a/trigo-web/app/src/views/TrigoTreeTestView.vue b/trigo-web/app/src/views/TrigoTreeTestView.vue index 91b0e6327f8a05d061b53f922afcbe621d725c89..cf5296051e842558e0c4d15150af50917aa4acf5 100644 --- a/trigo-web/app/src/views/TrigoTreeTestView.vue +++ b/trigo-web/app/src/views/TrigoTreeTestView.vue @@ -158,7 +158,7 @@ > {{ move.notation }} parent={{ move.parentPos }}, leaf={{ move.leafPos }}leaf={{ move.leafPos }}, parent={{ treeVisualization.parent[move.leafPos] }} @@ -264,7 +264,7 @@ import type { Move } from "../../../inc/trigo/types"; // Configuration - const modelPath = "/onnx/GPT2CausalLM_ep0015_evaluation.onnx"; + const modelPath = import.meta.env.VITE_ONNX_TREE_MODEL; const vocabSize = 259; // State @@ -285,7 +285,8 @@ const treeVisualization = ref<{ evaluatedIds: number[]; mask: number[]; - moveData: Array<{ notation: string; parentPos: number; leafPos: number }>; + parent: Array; + moveData: Array<{ notation: string; leafPos: number }>; } | null>(null); // Computed properties @@ -329,6 +330,16 @@ // Compute sum of exp scores const sumExp = expScores.reduce((sum, exp) => sum + exp, 0); + // Handle edge case where all probabilities underflow to 0 + if (sumExp === 0 || !isFinite(sumExp)) { + console.warn("Probability sum is 0 or non-finite, using uniform distribution"); + const uniformProb = 1.0 / scoredMoves.value.length; + return scoredMoves.value.map((move) => ({ + ...move, + probability: uniformProb + })); + } + // Return moves with normalized probabilities return scoredMoves.value.map((move, i) => ({ ...move, @@ -416,9 +427,9 @@ treeVisualization.value = { evaluatedIds: treeStructure.evaluatedIds, mask: treeStructure.mask, + parent: treeStructure.parent, moveData: treeStructure.moveData.map((m) => ({ notation: m.notation, - parentPos: m.parentPos, leafPos: m.leafPos })) }; diff --git a/trigo-web/app/src/views/TrigoView.vue b/trigo-web/app/src/views/TrigoView.vue index 777d670463bda5dcceebc86da7f967a34d5719a0..2d038e0ae92be30dcbc549c0f91e8abd8c346939 100644 --- a/trigo-web/app/src/views/TrigoView.vue +++ b/trigo-web/app/src/views/TrigoView.vue @@ -41,14 +41,43 @@
Room: - ABC123 + {{ playerStore.roomId || "---" }} +
+
- You (Black) +
+ +
+ vs - Waiting... + +
+ + Waiting... +
- 🟢 Connected + + + {{ connectionStatusIcon }} {{ connectionStatusText }} +
@@ -62,6 +91,60 @@ + + +
+ + +
+ +
+ + +
+ +
+ + + + + +
@@ -213,36 +296,6 @@
- - -
-

Settings

-
-
- - -
- -
-
@@ -281,7 +334,11 @@ import { useRoute } from "vue-router"; import { TrigoViewport } from "@/services/trigoViewport"; import { useGameStore } from "@/stores/gameStore"; + import { usePlayerStore } from "@/stores/playerStore"; import { useTrigoAgent } from "@/composables/useTrigoAgent"; + import { useSocket } from "@/composables/useSocket"; + import { useRoomHash } from "@/composables/useRoomHash"; + import InlineNicknameEditor from "@/components/InlineNicknameEditor.vue"; import { storeToRefs } from "pinia"; import type { BoardShape } from "../../../inc/trigo"; import { Stone, validateMove, StoneType, validateTGN } from "../../../inc/trigo"; @@ -293,6 +350,31 @@ const route = useRoute(); const gameMode = computed(() => (route.meta.mode as string) || "single"); + // Game store + const gameStore = useGameStore(); + const { + game, + currentPlayer, + moveHistory, + currentMoveIndex, + capturedStones, + gameStatus, + boardShape, + moveCount, + isGameActive, + passCount + } = storeToRefs(gameStore); + + // Player store (for multiplayer) + const playerStore = usePlayerStore(); + + // Socket.io (for multiplayer) + const socketApi = useSocket(); + + // Room hash management (for VS People mode) + const { getRoomIdFromHash, updateHash, clearHash, isValidRoomId } = useRoomHash(); + const isJoiningRoom = ref(false); + // Helper functions for board shape parsing const parseBoardShape = (shapeStr: string): BoardShape => { const parts = shapeStr @@ -312,21 +394,6 @@ return encodeAb0yz([move.x, move.y, move.z], [shape.x, shape.y, shape.z]); }; - // Use game store - const gameStore = useGameStore(); - const { - currentPlayer, - moveHistory, - currentMoveIndex, - capturedStones, - gameStatus, - boardShape, - moveCount, - isGameActive, - passCount - } = storeToRefs(gameStore); - - // AI Agent (for VS AI mode) const aiAgent = useTrigoAgent(); const { isReady: aiReady, isThinking: aiThinking, error: aiError, lastMoveTime } = aiAgent; @@ -340,6 +407,19 @@ const aiPlayerColor = ref<"black" | "white">(loadAIColor()); const humanPlayerColor = computed(() => (aiPlayerColor.value === "white" ? "black" : "white")); + // VS People color preference with storage persistence + const loadPreferredColor = (): "black" | "white" => { + const stored = storage.getString("vs-people-color-preference"); + return stored === "black" || stored === "white" ? stored : "black"; + }; + + const preferredColor = ref<"black" | "white">(loadPreferredColor()); + + // Watch for color preference changes and persist to storage + watch(preferredColor, (newColor) => { + storage.setString("vs-people-color-preference", newColor); + }); + // Local state const hoveredPosition = ref(null); const blackScore = ref(0); @@ -497,29 +577,39 @@ // Apply AI move if valid if (aiMove) { - console.log(`[TrigoView] AI suggests move at (${aiMove.x}, ${aiMove.y}, ${aiMove.z})`); - - // Make move in store - const result = gameStore.makeMove(aiMove.x, aiMove.y, aiMove.z); - - if (result.success && viewport) { - // Add AI stone to viewport - const stoneColor = gameStore.opponentPlayer; - viewport.addStone(aiMove.x, aiMove.y, aiMove.z, stoneColor); - - // Remove captured stones - if (result.capturedPositions && result.capturedPositions.length > 0) { - result.capturedPositions.forEach((pos: any) => { - viewport.removeStone(pos.x, pos.y, pos.z); - }); - console.log(`[TrigoView] AI captured ${result.capturedPositions.length} stone(s)`); + // Check if AI chose to pass + if (aiMove.isPass) { + console.log("[TrigoView] AI chose to pass"); + const result = gameStore.pass(); + if (result.success) { + console.log(`[TrigoView] AI pass applied successfully (${lastMoveTime.value.toFixed(0)}ms)`); + } else { + console.error("[TrigoView] Failed to apply AI pass"); } + } else { + console.log(`[TrigoView] AI suggests move at (${aiMove.x}, ${aiMove.y}, ${aiMove.z})`); + + // Make move in store + const result = gameStore.makeMove(aiMove.x, aiMove.y, aiMove.z); + + if (result.success && viewport) { + // Add AI stone to viewport + const stoneColor = gameStore.opponentPlayer; + viewport.addStone(aiMove.x, aiMove.y, aiMove.z, stoneColor); + + // Remove captured stones + if (result.capturedPositions && result.capturedPositions.length > 0) { + result.capturedPositions.forEach((pos: any) => { + viewport.removeStone(pos.x, pos.y, pos.z); + }); + console.log(`[TrigoView] AI captured ${result.capturedPositions.length} stone(s)`); + } - console.log(`[TrigoView] AI move applied successfully (${lastMoveTime.value.toFixed(0)}ms)`); + console.log(`[TrigoView] AI move applied successfully (${lastMoveTime.value.toFixed(0)}ms)`); + } } } else { - console.log("[TrigoView] AI chose to pass"); - // Handle AI pass if needed + console.log("[TrigoView] AI returned no move"); } } catch (err) { console.error("[TrigoView] Failed to generate AI move:", err); @@ -527,11 +617,39 @@ }; + // Sync viewport with current game state (for undo/redo/reset) + const syncViewportWithGame = () => { + if (!viewport) return; + + // Get all stones from game store and add to viewport + const stones = gameStore.getAllStones(); + for (const stone of stones) { + viewport.addStone(stone.x, stone.y, stone.z, stone.color); + } + + console.log(`[TrigoView] Synced viewport with ${stones.length} stones`); + }; + + // Handle stone placement const handleStoneClick = async (x: number, y: number, z: number) => { if (!gameStarted.value) return; - // Make move in store + // VS People mode: validate turn and emit socket event + if (gameMode.value === "vs-people") { + // Check if it's the local player's turn + if (!isLocalPlayerTurn.value) { + console.log("[TrigoView] Not your turn, ignoring click"); + return; + } + + // Emit move to server (server will validate and broadcast) + console.log(`[TrigoView] Emitting makeMove: (${x}, ${y}, ${z})`); + socketApi.makeMove(x, y, z); + return; + } + + // Single player and VS AI mode: make move locally const result = gameStore.makeMove(x, y, z); if (result.success && viewport) { @@ -656,7 +774,40 @@ } }; + // Reset game for multiplayer (VS People mode) + const resetMultiplayerGame = () => { + if (!confirm("Reset the game? This will clear all moves.")) return; + + const shape = parseBoardShape(selectedBoardShape.value); + const swapColors = playerStore.playerColor !== preferredColor.value; + + socketApi.socket.emit("resetGame", { + boardShape: shape, + swapColors: swapColors + }, (response: any) => { + if (response.success) { + console.log("Multiplayer game reset successfully"); + // Local reset is handled by gameReset event listener + } else { + console.error("Reset failed:", response.error); + alert(`Failed to reset game: ${response.error || "Unknown error"}`); + } + }); + }; + const pass = () => { + // VS People mode: validate turn and emit socket event + if (gameMode.value === "vs-people") { + if (!isLocalPlayerTurn.value) { + console.log("[TrigoView] Not your turn, cannot pass"); + return; + } + console.log("[TrigoView] Emitting pass"); + socketApi.pass(); + return; + } + + // Single player and VS AI mode: pass locally const previousPlayer = currentPlayer.value; const success = gameStore.pass(); @@ -840,6 +991,265 @@ } }; + // ===== VS People Mode: Nickname Management ===== + + // Computed properties for multiplayer + const opponentPlayerColor = computed(() => { + if (!playerStore.playerColor) return null; + return playerStore.playerColor === "black" ? "white" : "black"; + }); + + const isLocalPlayerTurn = computed(() => { + return currentPlayer.value === playerStore.playerColor; + }); + + const connectionStatusClass = computed(() => { + return playerStore.connectionStatus === "in-room" ? "connected" : "disconnected"; + }); + + const connectionStatusText = computed(() => { + if (playerStore.connectionStatus === "in-room") return "Connected"; + if (playerStore.connectionStatus === "connected") return "Joining room..."; + return "Disconnected"; + }); + + const connectionStatusIcon = computed(() => { + if (playerStore.connectionStatus === "in-room") return "🟢"; + if (playerStore.connectionStatus === "connected") return "🟡"; // Yellow while joining + return "🔴"; + }); + + // Update local player nickname + const updateLocalNickname = (newNickname: string) => { + const success = playerStore.setNickname(newNickname); + + if (!success) { + console.error("Failed to update nickname"); + return; + } + + // If in a room, broadcast nickname change to opponents + if (playerStore.isInRoom) { + socketApi.changeNickname(newNickname, (response: any) => { + if (!response.success) { + console.error("Failed to broadcast nickname change:", response.error); + alert(`Failed to change nickname: ${response.error}`); + } + }); + } + }; + + // Copy room code to clipboard + const copyRoomCode = () => { + if (!playerStore.roomId) return; + + navigator.clipboard + .writeText(playerStore.roomId) + .then(() => alert("Room code copied to clipboard!")) + .catch((err) => console.error("Failed to copy room code:", err)); + }; + + + // ===== Room Management Functions (VS People Mode) ===== + + /** + * Wait for socket connection with timeout + * Prevents race condition where hash is read before socket connects + */ + async function waitForSocketConnection(timeout = 5000): Promise { + return new Promise((resolve, reject) => { + if (socketApi.socket.connected) { + resolve(); + return; + } + + const checkInterval = setInterval(() => { + if (socketApi.socket.connected) { + clearInterval(checkInterval); + clearTimeout(timeoutHandle); + resolve(); + } + }, 100); + + const timeoutHandle = setTimeout(() => { + clearInterval(checkInterval); + reject(new Error("Socket connection timeout")); + }, timeout); + }); + } + + + /** + * Initialize multiplayer room based on URL hash + * - No hash: create new room + * - Valid hash: join existing room + * - Invalid hash: redirect to create new room + */ + async function initializeMultiplayerRoom() { + console.log("[TrigoView] ========== INITIALIZE MULTIPLAYER ROOM =========="); + console.log("[TrigoView] isJoiningRoom:", isJoiningRoom.value); + console.log("[TrigoView] Socket connected:", socketApi.socket.connected); + console.log("[TrigoView] Socket ID:", socketApi.socket.id); + + if (isJoiningRoom.value) { + console.log("[TrigoView] Already joining, skipping"); + return; // Prevent duplicate joins + } + + const hashRoomId = getRoomIdFromHash(); + console.log("[TrigoView] hashRoomId from URL:", hashRoomId); + + if (!hashRoomId) { + // No hash: create new room + console.log("[TrigoView] No hash found, creating new room"); + await createAndJoinRoom(); + } else if (isValidRoomId(hashRoomId)) { + // Valid hash: join existing room + console.log("[TrigoView] Valid hash found, joining room:", hashRoomId); + await joinRoomByHash(hashRoomId); + } else { + // Invalid hash: redirect to create new room + console.warn(`[TrigoView] Invalid room ID in hash: ${hashRoomId}`); + clearHash(); + await createAndJoinRoom(); + } + } + + + /** + * Create a new room and join it + */ + async function createAndJoinRoom() { + console.log("[TrigoView] createAndJoinRoom called, isJoiningRoom:", isJoiningRoom.value); + if (isJoiningRoom.value) { + console.log("[TrigoView] Already joining, returning"); + return; + } + isJoiningRoom.value = true; + console.log("[TrigoView] Set isJoiningRoom to true"); + + try { + console.log("[TrigoView] Waiting for socket connection..."); + await waitForSocketConnection(); + console.log("[TrigoView] Socket connection ready, calling joinRoom"); + + socketApi.joinRoom({ nickname: playerStore.nickname }, (response: any) => { + console.log("[TrigoView] joinRoom callback received:", response); + isJoiningRoom.value = false; + + if (response.success !== false && response.roomId) { + playerStore.joinRoom(response.roomId, response.playerColor); + updateHash(response.roomId); + console.log(`[TrigoView] Room created: ${response.roomId}`); + } else { + console.error("[TrigoView] Failed to create room:", response.error); + alert("Failed to create room. Please try again."); + } + }); + } catch (err) { + isJoiningRoom.value = false; + console.error("[TrigoView] Socket connection failed:", err); + alert("Connection failed. Please check your internet and try again."); + } + } + + + /** + * Join an existing room by room ID from hash + */ + async function joinRoomByHash(roomId: string) { + if (isJoiningRoom.value) return; + isJoiningRoom.value = true; + + try { + await waitForSocketConnection(); + + socketApi.joinRoom({ roomId, nickname: playerStore.nickname }, (response: any) => { + isJoiningRoom.value = false; + + if (response.success !== false && response.roomId) { + playerStore.joinRoom(response.roomId, response.playerColor); + console.log(`[TrigoView] Room joined: ${response.roomId}`); + + // Extract opponent from players list + if (response.players && socketApi.socket.id) { + for (const [playerId, player] of Object.entries(response.players) as [string, any][]) { + if (playerId !== socketApi.socket.id) { + playerStore.setOpponentNickname(player.nickname); + console.log(`[TrigoView] Opponent found: ${player.nickname}`); + break; + } + } + } + + // Start game if both players are present + const playerCount = response.players ? Object.keys(response.players).length : 0; + if (playerCount >= 2 && !gameStarted.value) { + gameStore.startGame(); + if (viewport) { + viewport.setGameActive(true); + } + console.log("[TrigoView] Game started - both players present"); + } + } else { + console.warn(`[TrigoView] Failed to join room:`, response.error); + handleJoinFailure(response.error || "Failed to join room"); + } + }); + } catch (err) { + isJoiningRoom.value = false; + console.error("[TrigoView] Socket connection failed:", err); + alert("Connection failed. Please check your internet and try again."); + } + } + + + /** + * Handle room join failure + * Clears invalid hash and creates new room + */ + function handleJoinFailure(error: string) { + clearHash(); + + if (error.includes("not found")) { + alert("Room not found. Creating a new room..."); + } else if (error.includes("full")) { + alert("This room is full. Creating a new room..."); + } else { + alert(`Failed to join room: ${error}. Creating a new room...`); + } + + createAndJoinRoom(); + } + + + /** + * Handle browser navigation (back/forward) and manual hash edits + */ + function handleHashChange() { + if (gameMode.value !== "vs-people") return; + + const currentHash = getRoomIdFromHash(); + const currentRoom = playerStore.roomId; + + if (currentHash !== currentRoom) { + if (currentHash && isValidRoomId(currentHash)) { + // User navigated to different room hash + if (currentRoom) socketApi.leaveRoom(); + joinRoomByHash(currentHash); + } else { + // Invalid hash or removed hash + if (currentRoom) { + updateHash(currentRoom); // Restore correct hash + } else { + createAndJoinRoom(); // Create new room + } + } + } + } + + // ===== Watchers ===== + // Watch for current player changes to update viewport preview watch(currentPlayer, (newPlayer) => { if (viewport) { @@ -862,6 +1272,20 @@ }); }); + // Watch playerStore.roomId to sync URL hash (VS People mode) + watch( + () => playerStore.roomId, + (newRoomId) => { + if (gameMode.value !== "vs-people") return; + + if (newRoomId) { + updateHash(newRoomId); + } else { + clearHash(); + } + } + ); + // Watch TGN modal to populate editable content when opened watch(showTGNModal, (isVisible) => { if (isVisible) { @@ -876,8 +1300,12 @@ onMounted(() => { console.log("TrigoDemo component mounted"); - // Try to restore game state from session storage - const restoredFromStorage = gameStore.loadFromSessionStorage(); + // In VS People mode, always start fresh (server maintains authoritative state) + // In other modes, try to restore from session storage + let restoredFromStorage = false; + if (gameMode.value !== "vs-people") { + restoredFromStorage = gameStore.loadFromSessionStorage(); + } // If not restored from storage, initialize new game if (!restoredFromStorage) { @@ -947,6 +1375,171 @@ }); } + // Setup socket listeners for VS People mode + if (gameMode.value === "vs-people") { + console.log("[TrigoView] Setting up socket listeners for VS People mode..."); + + // Listen for nickname changes + socketApi.onNicknameChanged((data) => { + console.log( + `[TrigoView] Player ${data.playerId} changed nickname: ${data.oldNickname} -> ${data.nickname}` + ); + + // If it's not us, update opponent nickname + if (data.playerId !== socketApi.socket.id) { + playerStore.setOpponentNickname(data.nickname); + } + }); + + // Listen for game updates (moves, passes, undo, redo) + socketApi.onGameUpdate((data) => { + console.log("[TrigoView] Game update received:", data); + + // Update current player from server + if (data.currentPlayer) { + gameStore.setCurrentPlayer(data.currentPlayer); + } + + // Handle different actions + if (data.action === "move" && data.lastMove) { + const { x, y, z } = data.lastMove; + // Apply move to local game state + const result = gameStore.makeMove(x, y, z); + + if (result.success && viewport) { + // Add stone to viewport + const stoneColor = gameStore.opponentPlayer; + viewport.addStone(x, y, z, stoneColor); + + // Remove captured stones + if (data.capturedPositions && data.capturedPositions.length > 0) { + data.capturedPositions.forEach((pos: { x: number; y: number; z: number }) => { + viewport.removeStone(pos.x, pos.y, pos.z); + }); + console.log(`[TrigoView] Captured ${data.capturedPositions.length} stone(s)`); + } + + // Hide territory visualization + viewport.hideDomainCubes(); + showTerritoryMode.value = false; + } + } else if (data.action === "pass") { + gameStore.pass(); + console.log("[TrigoView] Pass received from server"); + } else if (data.action === "undo" || data.action === "redo") { + // For undo/redo, reload game from TGN + if (data.tgn && viewport) { + gameStore.loadFromTGN(data.tgn); + // Resync viewport with game state + viewport.clearBoard(); + syncViewportWithGame(); + } + } + }); + + // Listen for player joined + socketApi.onPlayerJoined((data) => { + console.log("[TrigoView] Player joined:", data); + playerStore.setOpponentNickname(data.nickname); + + // Start game when second player joins + if (!gameStarted.value) { + gameStore.startGame(); + if (viewport) { + viewport.setGameActive(true); + } + console.log("[TrigoView] Game started - opponent joined"); + } + }); + + // Listen for player left + socketApi.onPlayerLeft((data) => { + console.log("[TrigoView] Player left:", data); + playerStore.setOpponentNickname(null); + }); + + // Listen for player disconnected + socketApi.onPlayerDisconnected((data) => { + console.log("[TrigoView] Player disconnected:", data); + if (data.playerId !== socketApi.socket.id) { + playerStore.setOpponentNickname(null); + } + }); + + // Listen for game ended + socketApi.onGameEnded((data) => { + console.log("[TrigoView] Game ended:", data); + alert(`Game ended! Winner: ${data.winner || "None"}\nReason: ${data.reason}`); + }); + + // Listen for game reset + socketApi.onGameReset((data) => { + console.log("[TrigoView] Game reset:", data); + // Reload game state + if (data.tgn) { + gameStore.loadFromTGN(data.tgn); + } else { + gameStore.resetGame(boardShape.value); + } + if (viewport) { + viewport.clearBoard(); + } + // Update player colors if changed + if (data.players && socketApi.socket.id) { + const myPlayer = data.players[socketApi.socket.id]; + if (myPlayer) { + playerStore.playerColor = myPlayer.color; + } + } + }); + + // Listen for errors + socketApi.onError((data) => { + console.error("[TrigoView] Socket error:", data.message); + }); + + // Set player ID when socket connects + if (socketApi.socket.id) { + playerStore.setPlayerId(socketApi.socket.id); + } + + // Listen for browser navigation (back/forward) and manual hash edits + window.addEventListener("hashchange", handleHashChange); + + // Handle socket connection and reconnection + socketApi.socket.on("connect", () => { + console.log("[TrigoView] Socket connected in VS People mode"); + + // Update player ID and connection status + if (socketApi.socket.id) { + playerStore.setPlayerId(socketApi.socket.id); + } + + // Initialize room based on URL hash (only if not already in a room) + if (!playerStore.roomId && !isJoiningRoom.value) { + console.log("[TrigoView] Initializing room after socket connection"); + initializeMultiplayerRoom(); + } + }); + + // Handle socket disconnection + socketApi.socket.on("disconnect", () => { + console.log("[TrigoView] Socket disconnected in VS People mode"); + // Don't reset roomId - we want to rejoin on reconnect + if (playerStore.roomId) { + playerStore.connectionStatus = "connected"; // Still have room info, just disconnected + } else { + playerStore.disconnect(); + } + }); + + // If socket is already connected, initialize room immediately + if (socketApi.socket.connected) { + console.log("[TrigoView] Socket already connected, initializing room"); + initializeMultiplayerRoom(); + } + } + // Add keyboard shortcuts window.addEventListener("keydown", handleKeyPress); }); @@ -978,6 +1571,22 @@ // Remove keyboard shortcuts window.removeEventListener("keydown", handleKeyPress); + // Cleanup socket listeners for VS People mode + if (gameMode.value === "vs-people") { + console.log("[TrigoView] Cleaning up socket listeners..."); + socketApi.offNicknameChanged(); + socketApi.offGameUpdate(); + socketApi.offPlayerJoined(); + socketApi.offPlayerLeft(); + socketApi.offPlayerDisconnected(); + socketApi.offGameEnded(); + socketApi.offGameReset(); + socketApi.offError(); + + // Remove hashchange listener + window.removeEventListener("hashchange", handleHashChange); + } + // Disconnect ResizeObserver if (resizeObserver) { resizeObserver.disconnect(); @@ -1081,7 +1690,7 @@ .view-header { display: flex; - justify-content: center; + justify-content: space-between; align-items: center; padding: 1rem 2rem; background: linear-gradient(135deg, #505050 0%, #454545 100%); @@ -1225,6 +1834,21 @@ font-size: 1.1rem; } + .btn-copy-room { + padding: 0.25rem 0.5rem; + background-color: transparent; + border: 1px solid #606060; + border-radius: 4px; + cursor: pointer; + font-size: 0.9rem; + transition: all 0.2s ease; + + &:hover { + background-color: #505050; + border-color: #808080; + } + } + .players-info { display: flex; align-items: center; @@ -1234,11 +1858,24 @@ border-radius: 8px; } + .player-display { + transition: all 0.3s ease; + + &.on-turn { + filter: brightness(1.3); + } + } + .player-name { color: #e0e0e0; font-weight: 600; } + .waiting-text { + color: #9ca3af; + font-style: italic; + } + .vs-divider { color: #606060; font-weight: 500; @@ -1325,6 +1962,68 @@ } } + .header-controls { + display: flex; + align-items: center; + gap: 0.75rem; + margin-left: auto; + + .board-shape-select, + .color-preference-select { + background: rgba(0, 0, 0, 0.3); + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 4px; + color: #fff; + padding: 0.4rem 0.6rem; + font-size: 0.9rem; + cursor: pointer; + transition: background 0.2s; + + &:hover { + background: rgba(0, 0, 0, 0.4); + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + // Ensure dropdown options are readable + option { + background: #3a3a3a; + color: #e0e0e0; + } + } + + .color-preference-select { + min-width: 130px; + } + + .board-shape-select { + min-width: 90px; + } + + .btn-reset { + background: #e94560; + color: white; + border: none; + border-radius: 4px; + padding: 0.4rem 1rem; + font-size: 0.9rem; + font-weight: 600; + cursor: pointer; + transition: background 0.2s; + + &:hover { + background: #d63850; + } + + &:active { + background: #c02040; + } + } + } + .view-body { display: flex; flex: 1; @@ -1690,68 +2389,6 @@ } } - .settings-section { - .settings-content { - display: flex; - flex-direction: column; - gap: 1rem; - - .setting-item { - display: flex; - align-items: center; - justify-content: space-between; - - label { - font-weight: 600; - color: #a0a0a0; - display: flex; - align-items: center; - gap: 0.3rem; - - .dirty-indicator { - color: #e94560; - font-size: 1.2rem; - font-weight: 700; - animation: pulse 2s ease-in-out infinite; - } - } - - select { - padding: 0.5rem; - border: 2px solid #505050; - border-radius: 8px; - background-color: #484848; - color: #e0e0e0; - cursor: pointer; - font-weight: 600; - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } - } - } - - .btn-new-game { - width: 100%; - padding: 0.75rem; - background-color: #e94560; - color: #fff; - border: none; - border-radius: 8px; - font-weight: 700; - cursor: pointer; - transition: all 0.3s ease; - text-transform: uppercase; - - &:hover { - background-color: #f95670; - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(233, 69, 96, 0.4); - } - } - } - } } } } diff --git a/trigo-web/app/vite.config.ts b/trigo-web/app/vite.config.ts index 8a593078cc916c5c13dd02d39aab808060e810c4..060619ee0c7c7ee279b3ae317c59a2798787f0a2 100644 --- a/trigo-web/app/vite.config.ts +++ b/trigo-web/app/vite.config.ts @@ -4,10 +4,12 @@ import { fileURLToPath, URL } from "node:url"; // https://vitejs.dev/config/ export default defineConfig(({ mode }) => { - // Load env file based on `mode` in the current working directory. - const env = loadEnv(mode, process.cwd(), ""); + // Load env file from repository root (parent directory) + const env = loadEnv(mode, fileURLToPath(new URL("..", import.meta.url)), ""); return { + // Load .env files from repository root + envDir: "..", plugins: [ vue(), // Plugin to set correct MIME types diff --git a/trigo-web/backend/.env b/trigo-web/backend/.env index bf323eefdfed33a090fa024a1f3108ddbe53f893..6ddec024a0c421820a117c98a09ed51b22931e54 100644 --- a/trigo-web/backend/.env +++ b/trigo-web/backend/.env @@ -1,3 +1,9 @@ -PORT=3000 -CLIENT_URL=http://localhost:5173 -NODE_ENV=development \ No newline at end of file +# ============================================================================ +# Backend Server Configuration +# ============================================================================ +# This file is DEPRECATED - all configuration is now in the root .env file +# Backend should load environment variables from ../. env (repository root) +# +# To configure the backend, edit: ../. env +# ============================================================================ + diff --git a/trigo-web/backend/.env.local b/trigo-web/backend/.env.local new file mode 100644 index 0000000000000000000000000000000000000000..1fd6feb66760ed7f7aaffc1ee90d24aeb6086eca --- /dev/null +++ b/trigo-web/backend/.env.local @@ -0,0 +1,2 @@ + +PORT=8157 diff --git a/trigo-web/backend/package-lock.json b/trigo-web/backend/package-lock.json index 5f37579f14a180c63bacb57346764020a8610558..e939a9d2551bcfeb37fa572c0d91d6f8393a226a 100644 --- a/trigo-web/backend/package-lock.json +++ b/trigo-web/backend/package-lock.json @@ -22,6 +22,7 @@ "@types/node": "^20.5.0", "nodemon": "^3.0.1", "ts-node": "^10.9.1", + "tsx": "^4.20.6", "typescript": "^5.2.2" } }, @@ -37,6 +38,422 @@ "node": ">=12" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -613,6 +1030,47 @@ "node": ">= 0.4" } }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -773,6 +1231,18 @@ "node": ">= 0.4" } }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -1188,6 +1658,15 @@ "node": ">=8.10.0" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1552,6 +2031,25 @@ } } }, + "node_modules/tsx": { + "version": "4.20.6", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "dev": true, + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", diff --git a/trigo-web/backend/package.json b/trigo-web/backend/package.json index caaa09616c4178279bf3971df32fdc0ede54c8bf..0dc5d5d5ef1f078411760d81b310059388cab8f9 100644 --- a/trigo-web/backend/package.json +++ b/trigo-web/backend/package.json @@ -1,10 +1,11 @@ { "name": "trigo-backend", "version": "1.0.0", + "type": "module", "description": "Backend server for Trigo game", "main": "dist/backend/src/server.js", "scripts": { - "dev": "nodemon --watch src --exec ts-node src/server.ts", + "dev": "nodemon --watch src --exec tsx src/server.ts", "build": "tsc", "start": "node dist/backend/src/server.js", "test": "echo \"Error: no test specified\" && exit 1" @@ -31,6 +32,7 @@ "@types/node": "^20.5.0", "nodemon": "^3.0.1", "ts-node": "^10.9.1", + "tsx": "^4.20.6", "typescript": "^5.2.2" } } diff --git a/trigo-web/backend/src/server.ts b/trigo-web/backend/src/server.ts index eddd593a534ec63e4fd8540fbed9823a3893593d..cc1ff87b773a525d0c5b330a980d1336937de533 100644 --- a/trigo-web/backend/src/server.ts +++ b/trigo-web/backend/src/server.ts @@ -1,11 +1,42 @@ -const express = require("express"); -const { createServer } = require("http"); -const { Server } = require("socket.io"); -const cors = require("cors"); -const dotenv = require("dotenv"); -const path = require("path"); +import express from "express"; +import { createServer } from "http"; +import { Server } from "socket.io"; +import cors from "cors"; +import dotenv from "dotenv"; +import path from "path"; +import fs from "fs"; +import { fileURLToPath } from "url"; +import { GameManager } from "./services/gameManager"; +import { setupSocketHandlers } from "./sockets/gameSocket"; -dotenv.config(); +// Get __dirname equivalent in ES modules +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Load environment variables +// Priority: .env.local > .env (load .env first, then .env.local overwrites) +// When running via ts-node: __dirname = backend/src/, so go up 1 level +// When running compiled: __dirname = dist/backend/src/, so go up 3 levels +const isDev = __dirname.includes("/src") && !__dirname.includes("/dist"); +const levelsUp = isDev ? "../" : "../../../"; +const envPath = path.join(__dirname, levelsUp, ".env"); +const envLocalPath = path.join(__dirname, levelsUp, ".env.local"); + +// Load .env first (base configuration) +if (fs.existsSync(envPath)) { + dotenv.config({ path: envPath }); + console.log("[Config] Loaded .env"); +} else { + console.log(`[Config] .env not found at: ${envPath}`); +} + +// Load .env.local second (overrides .env for local development) +if (fs.existsSync(envLocalPath)) { + dotenv.config({ path: envLocalPath, override: true }); + console.log("[Config] Loaded .env.local (overriding .env)"); +} else { + console.log(`[Config] .env.local not found at: ${envLocalPath}`); +} const app = express(); const httpServer = createServer(app); @@ -20,8 +51,17 @@ const io = new Server(httpServer, { } }); -const PORT = process.env.PORT || 3000; -const HOST = process.env.HOST || "localhost"; +// Create GameManager instance +const gameManager = new GameManager(); + +const PORT = parseInt(process.env.PORT || "3000", 10); +const HOST = process.env.HOST || "0.0.0.0"; + +console.log(`[Config] Server Configuration:`); +console.log(`[Config] PORT: ${PORT}`); +console.log(`[Config] HOST: ${HOST}`); +console.log(`[Config] NODE_ENV: ${process.env.NODE_ENV || "development"}`); +console.log(`[Config] CLIENT_URL: ${process.env.CLIENT_URL || "not set"}`); // Middleware app.use(cors()); @@ -50,7 +90,10 @@ app.get("/health", (_req: any, res: any) => { io.on("connection", (socket: any) => { console.log(`New client connected: ${socket.id}`); - // Echo test handler + // Setup game-related socket handlers + setupSocketHandlers(io, socket, gameManager); + + // Echo test handler (for testing) socket.on("echo", (data: any, callback: any) => { const timestamp = new Date().toISOString(); const responseMessage = `Hello from server! Received: "${data.message}" at ${timestamp}`; diff --git a/trigo-web/backend/src/services/gameManager.ts b/trigo-web/backend/src/services/gameManager.ts index a9cad4576fb175aa74f9a04310a514253427b2c6..94cbfb08813e28db908e076f660624399a696d0e 100644 --- a/trigo-web/backend/src/services/gameManager.ts +++ b/trigo-web/backend/src/services/gameManager.ts @@ -16,6 +16,7 @@ export interface GameState { export interface GameRoom { id: string; + adminId: string; // Room creator who has admin permissions players: { [playerId: string]: Player }; game: TrigoGame; // The actual game instance gameState: GameState; // Game status metadata @@ -32,17 +33,21 @@ export class GameManager { console.log("GameManager initialized"); } - createRoom(playerId: string, nickname: string, boardShape?: BoardShape): GameRoom | null { + createRoom(playerId: string, nickname: string, boardShape?: BoardShape, preferredColor?: "black" | "white"): GameRoom | null { const roomId = this.generateRoomId(); const shape = boardShape || this.defaultBoardShape; + // Use preferred color if specified, default to black + const playerColor = preferredColor || "black"; + const room: GameRoom = { id: roomId, + adminId: playerId, // Room creator is admin players: { [playerId]: { id: playerId, nickname, - color: "black", + color: playerColor, connected: true } }, @@ -72,7 +77,7 @@ export class GameManager { return room; } - joinRoom(roomId: string, playerId: string, nickname: string): GameRoom | null { + joinRoom(roomId: string, playerId: string, nickname: string, preferredColor?: "black" | "white"): GameRoom | null { const room = this.rooms.get(roomId); if (!room) { return null; @@ -83,11 +88,22 @@ export class GameManager { return null; // Room is full } - // Assign white color to the second player + // Try to assign preferred color if specified + const firstPlayer = Object.values(room.players)[0]; + let assignedColor: "black" | "white"; + + if (preferredColor && preferredColor !== firstPlayer.color) { + // Preferred color is available + assignedColor = preferredColor; + } else { + // Assign opposite of first player's color + assignedColor = firstPlayer.color === "black" ? "white" : "black"; + } + room.players[playerId] = { id: playerId, nickname, - color: "white", + color: assignedColor, connected: true }; @@ -207,6 +223,98 @@ export class GameManager { return room.game.undo(); } + + /** + * Redo the last undone move (forward in history) + */ + redoMove(roomId: string, playerId: string): boolean { + const room = this.rooms.get(roomId); + if (!room) return false; + + const player = room.players[playerId]; + if (!player) return false; + + if (room.gameState.gameStatus !== "playing") { + return false; + } + + return room.game.redo(); + } + + + /** + * Reset the game to initial state (new game in same room) + * Only admin can reset the game + */ + resetGame( + roomId: string, + adminId: string, + options?: { + boardShape?: BoardShape; + playerColors?: { [playerId: string]: "black" | "white" }; + } + ): { success: boolean; error?: string } { + const room = this.rooms.get(roomId); + if (!room) return { success: false, error: "Room not found" }; + + // Check admin permission + if (room.adminId !== adminId) { + return { success: false, error: "Only room admin can reset the game" }; + } + + const boardShape = options?.boardShape; + const playerColors = options?.playerColors; + + // Apply player color assignments if provided + if (playerColors) { + const playerIds = Object.keys(room.players); + for (const playerId of playerIds) { + if (playerColors[playerId]) { + room.players[playerId].color = playerColors[playerId]; + } + } + console.log(`Player colors assigned:`, playerColors); + } + + // If board shape is provided and different from current, create a new game + if (boardShape) { + const currentShape = room.game.getShape(); + if ( + boardShape.x !== currentShape.x || + boardShape.y !== currentShape.y || + boardShape.z !== currentShape.z + ) { + // Create new game with new board shape + room.game = new TrigoGame(boardShape, { + onStepAdvance: (_step, history) => { + console.log(`Step ${history.length}: Player made move`); + }, + onCapture: (captured) => { + console.log(`Captured ${captured.length} stones`); + }, + onWin: (winner) => { + console.log(`Game won by ${winner}`); + } + }); + console.log(`Game ${roomId} reset with new board shape: ${boardShape.x}x${boardShape.y}x${boardShape.z}`); + } else { + // Same shape, just reset + room.game.reset(); + console.log(`Game ${roomId} reset to initial state`); + } + } else { + // No shape provided, just reset + room.game.reset(); + console.log(`Game ${roomId} reset to initial state`); + } + + room.gameState.gameStatus = "playing"; + room.gameState.winner = null; + room.startedAt = new Date(); + + return { success: true }; + } + /** * Get game board state for a room */ diff --git a/trigo-web/backend/src/sockets/gameSocket.ts b/trigo-web/backend/src/sockets/gameSocket.ts index e28b7ac453bb06b0f357fdbb5f55e0cccd5bdbf7..b77c9e9e22e08644bbf5f50050c05550c226828a 100644 --- a/trigo-web/backend/src/sockets/gameSocket.ts +++ b/trigo-web/backend/src/sockets/gameSocket.ts @@ -5,52 +5,141 @@ export function setupSocketHandlers(io: Server, socket: Socket, gameManager: Gam console.log(`Setting up socket handlers for ${socket.id}`); // Join room - socket.on("joinRoom", (data: { roomId?: string; nickname: string }) => { - const { roomId, nickname } = data; + socket.on( + "joinRoom", + (data: { roomId?: string; nickname: string; preferredColor?: "black" | "white" }, callback?: (response: any) => void) => { + console.log("[gameSocket] joinRoom event received:", { + roomId: data.roomId, + nickname: data.nickname, + preferredColor: data.preferredColor, + hasCallback: !!callback, + socketId: socket.id + }); - // Create or join room - const room = roomId - ? gameManager.joinRoom(roomId, socket.id, nickname) - : gameManager.createRoom(socket.id, nickname); + const { roomId, nickname, preferredColor } = data; - if (room) { - socket.join(room.id); + // Try to create or join room + try { + let room; - // Get complete game data for frontend - const board = gameManager.getGameBoard(room.id); - const currentPlayer = gameManager.getCurrentPlayer(room.id); - const stats = gameManager.getGameStats(room.id); + if (roomId) { + // Joining existing room + const existingRoom = gameManager.getRoom(roomId); - socket.emit("roomJoined", { - roomId: room.id, - playerId: socket.id, - playerColor: room.players[socket.id]?.color, - gameState: { - board, - boardShape: room.game.getShape(), - currentPlayer, - moveHistory: room.game.getHistory(), - currentMoveIndex: room.game.getCurrentStep(), - capturedStones: { - black: stats?.capturedByBlack || 0, - white: stats?.capturedByWhite || 0 - }, - gameStatus: room.gameState.gameStatus, - winner: room.gameState.winner + if (!existingRoom) { + // Room doesn't exist + if (callback) { + callback({ + success: false, + error: "Room not found", + errorCode: "ROOM_NOT_FOUND" + }); + } + return; + } + + const playerCount = Object.keys(existingRoom.players).length; + if (playerCount >= 2) { + // Room is full + if (callback) { + callback({ + success: false, + error: "Room is full", + errorCode: "ROOM_FULL" + }); + } + return; + } + + room = gameManager.joinRoom(roomId, socket.id, nickname, preferredColor); + } else { + // Creating new room + room = gameManager.createRoom(socket.id, nickname, undefined, preferredColor); } - }); - // Notify other players - socket.to(room.id).emit("playerJoined", { - playerId: socket.id, - nickname: nickname - }); + if (room) { + socket.join(room.id); - console.log(`Player ${socket.id} joined room ${room.id}`); - } else { - socket.emit("error", { message: "Failed to join room" }); + // Get complete game data for frontend + const currentPlayer = gameManager.getCurrentPlayer(room.id); + const stats = gameManager.getGameStats(room.id); + const tgn = room.game.toTGN(); + + // Get player list with colors + const players: { [playerId: string]: { nickname: string; color: "black" | "white" } } = {}; + for (const [pid, player] of Object.entries(room.players)) { + players[pid] = { + nickname: player.nickname, + color: player.color + }; + } + + const response = { + success: true, + roomId: room.id, + playerId: socket.id, + playerColor: room.players[socket.id]?.color, + isAdmin: room.adminId === socket.id, + adminId: room.adminId, + players, // Include all players in room + gameState: { + boardShape: room.game.getShape(), + currentPlayer, + currentMoveIndex: room.game.getCurrentStep(), + capturedStones: { + black: stats?.capturedByBlack || 0, + white: stats?.capturedByWhite || 0 + }, + gameStatus: room.gameState.gameStatus, + winner: room.gameState.winner, + tgn + } + }; + + // Send response via callback + if (callback) { + console.log("[gameSocket] Sending response via callback:", { + roomId: response.roomId, + playerColor: response.playerColor + }); + callback(response); + } else { + // Fallback to event emit for backward compatibility + console.log("[gameSocket] No callback, using roomJoined emit"); + socket.emit("roomJoined", response); + } + + // Notify other players + socket.to(room.id).emit("playerJoined", { + playerId: socket.id, + nickname: nickname + }); + + console.log( + `Player ${socket.id} ${roomId ? "joined" : "created"} room ${room.id}` + ); + } else { + // Generic failure + if (callback) { + callback({ + success: false, + error: "Failed to join or create room", + errorCode: "UNKNOWN_ERROR" + }); + } + } + } catch (error) { + console.error(`Error in joinRoom handler:`, error); + if (callback) { + callback({ + success: false, + error: "Server error", + errorCode: "SERVER_ERROR" + }); + } + } } - }); + ); // Leave room socket.on("leaveRoom", () => { @@ -71,23 +160,23 @@ export function setupSocketHandlers(io: Server, socket: Socket, gameManager: Gam const room = gameManager.getPlayerRoom(socket.id); if (room && gameManager.makeMove(room.id, socket.id, data)) { // Get updated game data - const board = gameManager.getGameBoard(room.id); const currentPlayer = gameManager.getCurrentPlayer(room.id); const stats = gameManager.getGameStats(room.id); const lastStep = room.game.getLastStep(); + const tgn = room.game.toTGN(); // Broadcast game update to all players in the room io.to(room.id).emit("gameUpdate", { - board, currentPlayer, + action: "move", lastMove: data, capturedStones: { black: stats?.capturedByBlack || 0, white: stats?.capturedByWhite || 0 }, capturedPositions: lastStep?.capturedPositions, - moveHistory: room.game.getHistory(), - currentMoveIndex: room.game.getCurrentStep() + currentMoveIndex: room.game.getCurrentStep(), + tgn }); } else { socket.emit("error", { message: "Invalid move" }); @@ -99,12 +188,13 @@ export function setupSocketHandlers(io: Server, socket: Socket, gameManager: Gam const room = gameManager.getPlayerRoom(socket.id); if (room && gameManager.passTurn(room.id, socket.id)) { const currentPlayer = gameManager.getCurrentPlayer(room.id); + const tgn = room.game.toTGN(); io.to(room.id).emit("gameUpdate", { currentPlayer, action: "pass", - moveHistory: room.game.getHistory(), - currentMoveIndex: room.game.getCurrentStep() + currentMoveIndex: room.game.getCurrentStep(), + tgn }); // Check for consecutive passes (game end) @@ -130,6 +220,150 @@ export function setupSocketHandlers(io: Server, socket: Socket, gameManager: Gam } }); + + // Undo move + socket.on("undoMove", (callback?: (response: any) => void) => { + const room = gameManager.getPlayerRoom(socket.id); + + if (!room) { + if (callback) callback({ success: false, error: "Not in a room", errorCode: "NOT_IN_ROOM" }); + return; + } + + if (room.gameState.gameStatus !== "playing") { + if (callback) callback({ success: false, error: "Game not active", errorCode: "GAME_NOT_ACTIVE" }); + return; + } + + const success = gameManager.undoMove(room.id, socket.id); + + if (success) { + const currentPlayer = gameManager.getCurrentPlayer(room.id); + const stats = gameManager.getGameStats(room.id); + const tgn = room.game.toTGN(); + + io.to(room.id).emit("gameUpdate", { + currentPlayer, + action: "undo", + currentMoveIndex: room.game.getCurrentStep(), + capturedStones: { + black: stats?.capturedByBlack || 0, + white: stats?.capturedByWhite || 0 + }, + tgn + }); + + if (callback) callback({ success: true }); + } else { + if (callback) callback({ success: false, error: "Cannot undo", errorCode: "UNDO_FAILED" }); + } + }); + + + // Redo move + socket.on("redoMove", (callback?: (response: any) => void) => { + const room = gameManager.getPlayerRoom(socket.id); + + if (!room) { + if (callback) callback({ success: false, error: "Not in a room", errorCode: "NOT_IN_ROOM" }); + return; + } + + if (room.gameState.gameStatus !== "playing") { + if (callback) callback({ success: false, error: "Game not active", errorCode: "GAME_NOT_ACTIVE" }); + return; + } + + if (!room.game.canRedo()) { + if (callback) callback({ success: false, error: "Nothing to redo", errorCode: "NOTHING_TO_REDO" }); + return; + } + + const success = gameManager.redoMove(room.id, socket.id); + + if (success) { + const currentPlayer = gameManager.getCurrentPlayer(room.id); + const stats = gameManager.getGameStats(room.id); + const lastStep = room.game.getLastStep(); + const tgn = room.game.toTGN(); + + io.to(room.id).emit("gameUpdate", { + currentPlayer, + action: "redo", + lastMove: lastStep?.position, + capturedStones: { + black: stats?.capturedByBlack || 0, + white: stats?.capturedByWhite || 0 + }, + capturedPositions: lastStep?.capturedPositions, + currentMoveIndex: room.game.getCurrentStep(), + tgn + }); + + if (callback) callback({ success: true }); + } else { + if (callback) callback({ success: false, error: "Redo failed", errorCode: "REDO_FAILED" }); + } + }); + + + // Reset game + socket.on("resetGame", ( + data?: { + boardShape?: { x: number; y: number; z: number }; + playerColors?: { [playerId: string]: "black" | "white" }; + } | ((response: any) => void), + callback?: (response: any) => void + ) => { + const room = gameManager.getPlayerRoom(socket.id); + + if (!room) { + const cb = typeof data === 'function' ? data : callback; + if (cb) cb({ success: false, error: "Not in a room", errorCode: "NOT_IN_ROOM" }); + return; + } + + // Handle both old signature (no data) and new signature (with options) + const options = typeof data === 'object' && data !== null ? { + boardShape: data.boardShape, + playerColors: data.playerColors + } : undefined; + const responseCb = typeof data === 'function' ? data : callback; + + const result = gameManager.resetGame(room.id, socket.id, options); + + if (result.success) { + const currentPlayer = gameManager.getCurrentPlayer(room.id); + const tgn = room.game.toTGN(); + + // Get updated player info with new colors + const players: { [playerId: string]: { nickname: string; color: "black" | "white" } } = {}; + for (const [pid, player] of Object.entries(room.players)) { + players[pid] = { + nickname: player.nickname, + color: player.color + }; + } + + io.to(room.id).emit("gameReset", { + currentPlayer, + boardShape: room.game.getShape(), + currentMoveIndex: 0, + capturedStones: { black: 0, white: 0 }, + players, + tgn + }); + + if (responseCb) responseCb({ success: true }); + } else { + if (responseCb) responseCb({ + success: false, + error: result.error || "Reset failed", + errorCode: result.error === "Only room admin can reset the game" ? "NOT_ADMIN" : "RESET_FAILED" + }); + } + }); + // Chat messages socket.on("chatMessage", (data: { content: string }) => { const room = gameManager.getPlayerRoom(socket.id); @@ -143,6 +377,48 @@ export function setupSocketHandlers(io: Server, socket: Socket, gameManager: Gam } }); + // Change nickname + socket.on( + "changeNickname", + (data: { nickname: string }, callback?: (response: any) => void) => { + const room = gameManager.getPlayerRoom(socket.id); + + if (!room) { + const error = { success: false, error: "Not in a room" }; + if (callback) callback(error); + return; + } + + // Validate nickname + const validation = validateNickname(data.nickname); + if (!validation.valid) { + const error = { success: false, error: validation.error }; + if (callback) callback(error); + return; + } + + // Update player nickname + const player = room.players[socket.id]; + if (player) { + const oldNickname = player.nickname; + player.nickname = data.nickname; + + // Broadcast to all players in room + io.to(room.id).emit("nicknameChanged", { + playerId: socket.id, + nickname: data.nickname, + oldNickname: oldNickname + }); + + console.log(`Player ${socket.id} changed nickname: ${oldNickname} -> ${data.nickname}`); + + if (callback) { + callback({ success: true, nickname: data.nickname }); + } + } + } + ); + // Handle disconnection socket.on("disconnect", () => { console.log(`Client disconnected: ${socket.id}`); @@ -155,3 +431,33 @@ export function setupSocketHandlers(io: Server, socket: Socket, gameManager: Gam } }); } + + +/** + * Validate nickname according to the rules: + * - Length: 3-20 characters + * - Characters: alphanumeric + spaces only + * - No leading/trailing whitespace + */ +function validateNickname(nickname: string): { valid: boolean; error?: string } { + if (!nickname || typeof nickname !== "string") { + return { valid: false, error: "Invalid nickname" }; + } + + const trimmed = nickname.trim(); + + if (trimmed.length < 3) { + return { valid: false, error: "Nickname must be at least 3 characters" }; + } + if (trimmed.length > 20) { + return { valid: false, error: "Nickname must be 20 characters or less" }; + } + if (!/^[a-zA-Z0-9 ]+$/.test(trimmed)) { + return { valid: false, error: "Only letters, numbers, and spaces allowed" }; + } + if (trimmed !== nickname) { + return { valid: false, error: "No leading or trailing spaces allowed" }; + } + + return { valid: true }; +} diff --git a/trigo-web/inc/config.ts b/trigo-web/inc/config.ts new file mode 100644 index 0000000000000000000000000000000000000000..9765a1de6aa69c2547c9bd0360344914774debd1 --- /dev/null +++ b/trigo-web/inc/config.ts @@ -0,0 +1,181 @@ +/** + * Configuration Utilities + * + * Provides centralized access to environment variables for ONNX model paths + * and other configuration values. + * + * Works across different contexts: + * - Frontend (Vite): Uses import.meta.env.VITE_* variables + * - Backend/Tools (Node): Uses process.env.* variables with dotenv + */ + +import * as path from "path"; +import { fileURLToPath } from "url"; + + +// Environment detection +const isNode = typeof process !== "undefined" && process.versions?.node; +const isBrowser = typeof window !== "undefined"; + + +/** + * ONNX Session Options + */ +export interface OnnxSessionOptions { + executionProviders: string[]; + intraOpNumThreads?: number; + interOpNumThreads?: number; + graphOptimizationLevel?: "disabled" | "basic" | "extended" | "all"; + enableCpuMemArena?: boolean; + enableMemPattern?: boolean; +} + + +/** + * Get ONNX model paths + * Returns paths appropriate for the current environment + */ +export function getOnnxModelPaths(): { + evaluationModel: string; + treeModel: string; +} { + // Frontend (Vite environment) + if (isBrowser && typeof import.meta.env !== "undefined") { + const evaluationModel = import.meta.env.VITE_ONNX_EVALUATION_MODEL; + const treeModel = import.meta.env.VITE_ONNX_TREE_MODEL; + + if (!evaluationModel || !treeModel) { + throw new Error("ONNX model paths not configured. Check VITE_ONNX_EVALUATION_MODEL and VITE_ONNX_TREE_MODEL in .env"); + } + + return { evaluationModel, treeModel }; + } + + // Backend/Tools (Node environment) + if (isNode) { + const evaluationModel = process.env.ONNX_EVALUATION_MODEL; + const treeModel = process.env.ONNX_TREE_MODEL; + + if (!evaluationModel || !treeModel) { + throw new Error("ONNX model paths not configured. Check ONNX_EVALUATION_MODEL and ONNX_TREE_MODEL in .env"); + } + + return { evaluationModel, treeModel }; + } + + // Should not reach here + throw new Error("Unknown environment - cannot determine model paths"); +} + + +/** + * Get ONNX session options from environment variables + * Returns session options with threading and optimization configuration + */ +export function getOnnxSessionOptions(): OnnxSessionOptions { + // Default values + const options: OnnxSessionOptions = { + executionProviders: ["cpu"], + graphOptimizationLevel: "all", + enableCpuMemArena: true, + enableMemPattern: true + }; + + // Frontend (Vite environment) + if (isBrowser && typeof import.meta.env !== "undefined") { + const intraThreadsEnv = import.meta.env.VITE_ONNX_INTRA_OP_NUM_THREADS; + const interThreadsEnv = import.meta.env.VITE_ONNX_INTER_OP_NUM_THREADS; + const graphOptEnv = import.meta.env.VITE_ONNX_GRAPH_OPTIMIZATION_LEVEL; + + if (intraThreadsEnv) options.intraOpNumThreads = parseInt(intraThreadsEnv, 10); + if (interThreadsEnv) options.interOpNumThreads = parseInt(interThreadsEnv, 10); + if (graphOptEnv) options.graphOptimizationLevel = graphOptEnv as any; + } + + // Backend/Tools (Node environment) + if (isNode) { + const intraThreadsEnv = process.env.ONNX_INTRA_OP_NUM_THREADS; + const interThreadsEnv = process.env.ONNX_INTER_OP_NUM_THREADS; + const graphOptEnv = process.env.ONNX_GRAPH_OPTIMIZATION_LEVEL; + const cpuMemArenaEnv = process.env.ONNX_ENABLE_CPU_MEM_ARENA; + const memPatternEnv = process.env.ONNX_ENABLE_MEM_PATTERN; + + if (intraThreadsEnv) options.intraOpNumThreads = parseInt(intraThreadsEnv, 10); + if (interThreadsEnv) options.interOpNumThreads = parseInt(interThreadsEnv, 10); + if (graphOptEnv) options.graphOptimizationLevel = graphOptEnv as any; + if (cpuMemArenaEnv) options.enableCpuMemArena = cpuMemArenaEnv === "true"; + if (memPatternEnv) options.enableMemPattern = memPatternEnv === "true"; + } + + return options; +} + + +/** + * Get absolute path to model file (Node.js only) + * Resolves relative paths to absolute paths from project root + */ +export function getAbsoluteModelPath(relativePath: string): string { + if (!isNode) { + return relativePath; // Browser environment - return as-is + } + + // If already absolute, return as-is + if (path.isAbsolute(relativePath)) { + return relativePath; + } + + // Resolve relative to project root + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); + const rootDir = path.resolve(__dirname, ".."); + return path.resolve(rootDir, relativePath); +} + + +/** + * Load environment variables from .env file (Node.js only) + * Call this at the start of tool scripts + * + * Loading order: + * 1. .env (base configuration, committed to git) + * 2. .env.local (local overrides, not committed to git) + */ +export async function loadEnvConfig(): Promise { + if (!isNode) { + console.warn("[Config] loadEnvConfig() called in non-Node environment"); + return; + } + + try { + // Dynamically import dotenv (ESM-compatible) + const dotenv = (await import("dotenv")).default; + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); + const rootDir = path.resolve(__dirname, ".."); + + // Load base .env file + const baseResult = dotenv.config({ path: path.join(rootDir, ".env") }); + + if (baseResult.error) { + console.warn("[Config] Failed to load .env:", baseResult.error.message); + } else { + console.log("[Config] ✓ Base environment variables loaded from .env"); + } + + // Load .env.local for local overrides (if exists) + // Use override: true to allow .env.local to override .env values + const localResult = dotenv.config({ + path: path.join(rootDir, ".env.local"), + override: true + }); + + if (!localResult.error) { + console.log("[Config] ✓ Local overrides loaded from .env.local"); + } + // Silently ignore if .env.local doesn't exist (optional file) + + } catch (error) { + console.warn("[Config] dotenv not available:", error); + } +} diff --git a/trigo-web/inc/mctsAgent.ts b/trigo-web/inc/mctsAgent.ts new file mode 100644 index 0000000000000000000000000000000000000000..e99cfadc08faaa54aabbccb67fb21b17ee7d4615 --- /dev/null +++ b/trigo-web/inc/mctsAgent.ts @@ -0,0 +1,855 @@ +/** + * Monte Carlo Tree Search (MCTS) Agent for Trigo + * + * Implements AlphaGo Zero-style MCTS with: + * - PUCT (Polynomial Upper Confidence Trees) selection + * - Neural network guidance for policy and value + * - Visit count statistics for training data generation + * + * Based on: Silver et al., "Mastering the Game of Go without Human Knowledge" + */ + +import { TrigoGame } from "./trigo/game"; +import type { Move } from "./trigo/types"; +import { TrigoTreeAgent } from "./trigoTreeAgent"; +import { TrigoEvaluationAgent } from "./trigoEvaluationAgent"; + + +/** + * MCTS Configuration + */ +export interface MCTSConfig { + numSimulations: number; // Number of MCTS simulations per move (default: 600) + cPuct: number; // PUCT exploration constant (default: 1.0) + temperature: number; // Selection temperature for first 30 moves (default: 1.0) + dirichletAlpha: number; // Dirichlet noise alpha parameter (default: 0.03) + dirichletEpsilon: number; // Dirichlet noise mixing weight (default: 0.25) +} + + +/** + * MCTS Tree Node + * Stores search statistics for all legal actions from a given game state + * + * Memory optimization: Only root node stores the full game state. + * Non-root nodes only store the action that led to them. + * During simulation, a working state is cloned once and mutated along the path. + */ +interface MCTSNode { + state: TrigoGame | null; // Game state (only stored at root node for memory efficiency) + parent: MCTSNode | null; // Parent node (null for root) + action: Move | null; // Action that led to this node (null for root) + + // MCTS statistics per action (action key -> value) + N: Map; // Visit counts N(s,a) + W: Map; // Total action-value W(s,a) + Q: Map; // Mean action-value Q(s,a) = W(s,a) / N(s,a) + P: Map; // Prior probabilities P(s,a) from policy network + + children: Map; // Child nodes (action key -> child node) + expanded: boolean; // Whether this node has been expanded + terminalValue: number | null; // Cached terminal value (null if not terminal or not computed) + + // Terminal propagation optimization (GPT-5.1 suggestions) + depth: number; // Distance from root (0 for root) + playerToMove: number; // Player to move at this node (1=Black, 2=White) +} + + +/** + * MCTS Agent + * Combines tree search with neural network evaluation + */ +export class MCTSAgent { + private treeAgent: TrigoTreeAgent; // For policy priors + private evaluationAgent: TrigoEvaluationAgent; // For value evaluation + private config: MCTSConfig; + public debugMode: boolean = false; // Enable debug logging + + + constructor( + treeAgent: TrigoTreeAgent, + evaluationAgent: TrigoEvaluationAgent, + config: Partial = {} + ) { + this.treeAgent = treeAgent; + this.evaluationAgent = evaluationAgent; + + // Default configuration (AlphaGo Zero-inspired) + this.config = { + numSimulations: config.numSimulations ?? 600, + cPuct: config.cPuct ?? 1.0, + temperature: config.temperature ?? 1.0, + dirichletAlpha: config.dirichletAlpha ?? 0.03, + dirichletEpsilon: config.dirichletEpsilon ?? 0.25 + }; + } + + + /** + * Select best move using MCTS + * + * @param game Current game state + * @param moveNumber Move number (for temperature schedule) + * @returns Selected move with visit count statistics + */ + async selectMove(game: TrigoGame, moveNumber: number): Promise<{ + move: Move; + visitCounts: Map; + searchPolicy: Map; // Normalized visit counts π(a|s) + rootValue: number; + }> { + // Create root node + const root = this.createNode(game, null, null); + + // Check if root is already terminal (game over) + const terminalResult = this.checkTerminal(game); + if (terminalResult !== null) { + const currentPlayer = game.getCurrentPlayer(); + return { + move: { player: currentPlayer === 1 ? "black" : "white", isPass: true }, + visitCounts: new Map(), + searchPolicy: new Map(), + rootValue: terminalResult + }; + } + + // Run MCTS simulations + for (let i = 0; i < this.config.numSimulations; i++) { + await this.runSimulation(root, i); + } + + // Temperature schedule: τ=1 for first 30 moves, τ→0 afterward + const temperature = moveNumber < 30 ? this.config.temperature : 0.01; + + // Select move based on visit counts + const move = this.selectPlayAction(root, temperature); + + // Set correct player for returned move + const currentPlayer = game.getCurrentPlayer(); + move.player = currentPlayer === 1 ? "black" : "white"; + + // Compute search policy (normalized visit counts) + const searchPolicy = this.computeSearchPolicy(root, temperature); + + // Get root value estimate (average Q-value weighted by visit counts) + const rootValue = this.getRootValue(root); + + return { + move, + visitCounts: new Map(root.N), + searchPolicy, + rootValue + }; + } + + + /** + * Run a single MCTS simulation + * Select -> Expand & Evaluate -> Backup + * + * Memory optimization: Clone state once at start, mutate along path. + * This reduces memory from O(nodes) to O(simulations). + */ + private async runSimulation(root: MCTSNode, simIndex?: number): Promise { + // Invariant: root node must always have a non-null state + if (!root.state) { + throw new Error("runSimulation: root node must have a non-null state"); + } + + // Clone root state once for this simulation + const workingState = root.state.clone(); + + // 1. Selection: Traverse tree using PUCT until reaching unexpanded node + const { node, path } = this.select(root, workingState); + + // 2. Expand and Evaluate: Get value from neural network + const value = await this.expandAndEvaluate(node, workingState); + + // Debug logging + if (this.debugMode && simIndex !== undefined && simIndex < 10) { + const pathStr = path.map(p => p.actionKey).join(" → "); + const terminalStr = node.terminalValue !== null ? " [TERMINAL]" : ""; + console.log(`Sim ${simIndex + 1}: ${pathStr || "(root)"} → value=${value.toFixed(4)}${terminalStr}`); + } + + // 3. Backup: Propagate value up the tree + this.backup(path, value); + } + + + /** + * Selection phase: Traverse tree using PUCT + * + * @param root Root node to start selection from + * @param workingState Mutable game state that gets updated along the path + * @returns Leaf node and path taken + */ + private select(root: MCTSNode, workingState: TrigoGame): { + node: MCTSNode; + path: Array<{ node: MCTSNode; actionKey: string }>; + } { + const path: Array<{ node: MCTSNode; actionKey: string }> = []; + let node = root; + + // Traverse until we reach an unexpanded node + while (node.expanded) { + // GPT-5.1 recommendation: Stop at terminal nodes immediately + // Terminal nodes should not be expanded or evaluated further + if (node.terminalValue !== null) { + break; // Return terminal node, use its cached value + } + + // Get all legal actions + const actionKeys = Array.from(node.P.keys()); + + // Terminal node check: if expanded but no actions, this is a terminal node + if (actionKeys.length === 0) { + break; // Return this terminal node as leaf + } + + // Select action with best PUCT value + // Both players select HIGHEST PUCT value: + // - Black: PUCT = -Q + U, max PUCT = max(-Q) = min(Q) ✓ + // - White: PUCT = Q + U, max PUCT = max(Q) ✓ + const currentPlayer = workingState.getCurrentPlayer(); + const isWhite = currentPlayer === 2; + + let bestActionKey = actionKeys[0]; + let bestPuct = this.calculatePUCT(node, bestActionKey, isWhite); + + for (let i = 1; i < actionKeys.length; i++) { + const actionKey = actionKeys[i]; + const puct = this.calculatePUCT(node, actionKey, isWhite); + + if (puct > bestPuct) { + bestPuct = puct; + bestActionKey = actionKey; + } + } + + // Record path + path.push({ node, actionKey: bestActionKey }); + + // Apply action to working state (instead of cloning) + const action = this.decodeAction(bestActionKey); + if (action.isPass) { + workingState.pass(); + } else if (action.x !== undefined && action.y !== undefined && action.z !== undefined) { + workingState.drop({ x: action.x, y: action.y, z: action.z }); + } + + // Move to child (create if doesn't exist) + if (!node.children.has(bestActionKey)) { + // Create child node WITHOUT storing state (memory optimization) + const childNode = this.createNode(null, node, action); + node.children.set(bestActionKey, childNode); + } + + node = node.children.get(bestActionKey)!; + } + + return { node, path }; + } + + + /** + * Expand and evaluate leaf node using neural networks + * + * @param node Leaf node to expand + * @param workingState Current game state at this node (passed from simulation) + * @returns Value estimate from evaluation network + */ + private async expandAndEvaluate(node: MCTSNode, workingState: TrigoGame): Promise { + // Check if terminal value is already cached + if (node.terminalValue !== null) { + return node.terminalValue; + } + + // Check if game is over (terminal state) + const terminalValue = this.checkTerminal(workingState); + if (terminalValue !== null) { + // Mark terminal node as expanded with empty action set to prevent revisits + // Cache the terminal value to avoid repeated checks + node.expanded = true; + node.terminalValue = terminalValue; + node.P = new Map(); // No actions available (terminal) + node.N = new Map(); + node.W = new Map(); + node.Q = new Map(); + node.children = new Map(); + + return terminalValue; + } + + // Non-terminal state: expand with policy network and evaluate + // Get all valid moves + const currentPlayer = workingState.getCurrentPlayer() === 1 ? "black" : "white"; + const validPositions = workingState.validMovePositions(); + const moves: Move[] = validPositions.map(pos => ({ + x: pos.x, + y: pos.y, + z: pos.z, + player: currentPlayer + })); + moves.push({ player: currentPlayer, isPass: true }); + + // Get policy priors from tree agent + const scoredMoves = await this.treeAgent.scoreMoves(workingState, moves); + + // Convert log probabilities to probabilities and normalize (stable softmax) + const maxScore = Math.max(...scoredMoves.map(m => m.score)); + const expScores = scoredMoves.map(m => Math.exp(m.score - maxScore)); + const sumExp = expScores.reduce((sum, exp) => sum + exp, 0); + + // Initialize priors P(s,a) + node.P = new Map(); + node.N = new Map(); + node.W = new Map(); + node.Q = new Map(); + + // Handle edge case: if all scores are -Infinity or sumExp is 0/NaN + const useFallback = !isFinite(sumExp) || sumExp < 1e-10; + + for (let i = 0; i < scoredMoves.length; i++) { + const actionKey = this.encodeAction(scoredMoves[i].move); + + // Use uniform distribution as fallback if normalization fails + const prior = useFallback ? (1.0 / scoredMoves.length) : (expScores[i] / sumExp); + + node.P.set(actionKey, prior); + node.N.set(actionKey, 0); + node.W.set(actionKey, 0); + node.Q.set(actionKey, 0); + } + + // Add Dirichlet noise at root + if (node.parent === null) { + this.addDirichletNoise(node.P); + } + + // Mark as expanded + node.expanded = true; + + // Get value estimate from evaluation agent + const evaluation = await this.evaluationAgent.evaluatePosition(workingState); + + // Return value directly (value model returns white-positive by design) + return evaluation.value; + } + + + /** + * Backup phase: Propagate value up the tree + * + * White-positive minimax propagation: + * - All Q-values represent White's advantage (positive = White winning) + * - When all children are terminal, mark parent as terminal with minimax value: + * * White's turn: terminal_value = max(children terminal values) + * * Black's turn: terminal_value = min(children terminal values) + * + * Improvements (based on GPT-5.1 review): + * - Uses stored playerToMove instead of computing from depth + * - Uses stored depth instead of recomputing via parent walk + * + * @param path Path from root to leaf + * @param value Value to propagate (white-positive: positive = white winning) + */ + private backup(path: Array<{ node: MCTSNode; actionKey: string }>, value: number): void { + // Propagate value up the tree (white-positive throughout) + // No sign flipping needed - Q values are always white-positive + for (let i = path.length - 1; i >= 0; i--) { + const { node, actionKey } = path[i]; + + // Update statistics + const n = node.N.get(actionKey) ?? 0; + const w = node.W.get(actionKey) ?? 0; + + node.N.set(actionKey, n + 1); + node.W.set(actionKey, w + value); + node.Q.set(actionKey, (w + value) / (n + 1)); + + // ========== Terminal State Propagation ========== + // Check if this node should be marked as terminal + // Condition: node is fully expanded AND all children are terminal AND node itself not yet marked + if (node.expanded && node.terminalValue === null) { + const actionKeys = Array.from(node.P.keys()); + + // Skip propagation if no actions (already a terminal leaf, or error state) + if (actionKeys.length === 0) { + continue; + } + + // Check if ALL children are terminal + let allChildrenTerminal = true; + const childTerminalValues: number[] = []; + + for (const key of actionKeys) { + const child = node.children.get(key); + + // If child doesn't exist yet, not all children explored + if (!child) { + allChildrenTerminal = false; + break; + } + + // If child is not terminal, not all children terminal + if (child.terminalValue === null) { + allChildrenTerminal = false; + break; + } + + // Child is terminal, collect its value + childTerminalValues.push(child.terminalValue); + } + + // If all children are terminal, mark current node as terminal with minimax value + if (allChildrenTerminal && childTerminalValues.length > 0) { + // Use stored playerToMove instead of computing from depth (GPT-5.1 suggestion) + const isWhiteTurn = node.playerToMove === 2; // 2 = White, 1 = Black + + // Apply minimax: choose best child value from current player's perspective + let terminalValue: number; + + if (isWhiteTurn) { + // White maximizes Q-value (white-positive) + terminalValue = Math.max(...childTerminalValues); + } else { + // Black minimizes Q-value (white-positive) + terminalValue = Math.min(...childTerminalValues); + } + + // Mark this node as terminal with the minimax value + node.terminalValue = terminalValue; + + // Debug logging for terminal propagation + if (this.debugMode) { + const playerName = isWhiteTurn ? 'White' : 'Black'; + console.log( + `[Terminal Propagation] Node at depth ${node.depth} (${playerName}) marked terminal: ` + + `value=${terminalValue.toFixed(4)}, children=[${childTerminalValues.map(v => v.toFixed(2)).join(', ')}]` + ); + } + } + } + // ================================================ + } + } + + + /** + * Calculate PUCT value for action selection + * + * PUCT = Q(s,a) + U(s,a) [for White, who maximizes] + * PUCT = -Q(s,a) + U(s,a) [for Black, who minimizes] + * where U(s,a) = c_puct * P(s,a) * sqrt(Σ_b N(s,b)) / (1 + N(s,a)) + * + * @param node Current node + * @param actionKey Action to evaluate + * @param isWhite Whether current player is White + * @returns PUCT value + */ + private calculatePUCT(node: MCTSNode, actionKey: string, isWhite: boolean): number { + const Q = node.Q.get(actionKey) ?? 0; + const N = node.N.get(actionKey) ?? 0; + const P = node.P.get(actionKey) ?? 0; + + // Sum of all visit counts at this node + const totalN = Array.from(node.N.values()).reduce((sum, n) => sum + n, 0); + + // Exploration term: U(s,a) = c_puct * P(s,a) * sqrt(Σ_b N(s,b) + 1) / (1 + N(s,a)) + // +1 in sqrt to avoid zero exploration when node first expanded + const U = this.config.cPuct * P * Math.sqrt(totalN + 1) / (1 + N); + + // Black minimizes Q (flips sign), White maximizes Q + return (isWhite ? Q : -Q) + U; + } + + + /** + * Select action to play based on visit counts + * Uses temperature to control exploration vs exploitation + * + * @param node Root node + * @param temperature Selection temperature (τ=1 for exploration, τ→0 for greedy) + * @returns Selected move + */ + private selectPlayAction(node: MCTSNode, temperature: number): Move { + const actionKeys = Array.from(node.N.keys()); + + // Edge case: no actions available (unexpanded root or terminal state) + if (actionKeys.length === 0) { + // Fallback to priors if available + const priorKeys = Array.from(node.P.keys()); + if (priorKeys.length > 0) { + // Sample from prior distribution + const priors = priorKeys.map(key => node.P.get(key) ?? 0); + const sumP = priors.reduce((sum, p) => sum + p, 0); + if (sumP > 0) { + let rand = Math.random() * sumP; + for (let i = 0; i < priorKeys.length; i++) { + rand -= priors[i]; + if (rand <= 0) { + return this.decodeAction(priorKeys[i]); + } + } + return this.decodeAction(priorKeys[priorKeys.length - 1]); + } + // Uniform fallback + const randomIndex = Math.floor(Math.random() * priorKeys.length); + return this.decodeAction(priorKeys[randomIndex]); + } + // No actions at all - return Pass as last resort + return { player: "black", isPass: true }; + } + + if (temperature < 0.01) { + // Greedy: Select action with highest visit count + let bestActionKey = actionKeys[0]; + let bestN = node.N.get(bestActionKey) ?? 0; + + for (let i = 1; i < actionKeys.length; i++) { + const actionKey = actionKeys[i]; + const n = node.N.get(actionKey) ?? 0; + if (n > bestN) { + bestN = n; + bestActionKey = actionKey; + } + } + + return this.decodeAction(bestActionKey); + } else { + // Temperature-based sampling: π(a|s) ∝ N(s,a)^(1/τ) + const nValues = actionKeys.map(key => node.N.get(key) ?? 0); + const nPowered = nValues.map(n => Math.pow(n, 1 / temperature)); + const sumN = nPowered.reduce((sum, n) => sum + n, 0); + + // Handle edge case: if all visits are 0 or sum is invalid + if (!isFinite(sumN) || sumN <= 0) { + // Fallback to uniform random selection (or use priors) + const randomIndex = Math.floor(Math.random() * actionKeys.length); + return this.decodeAction(actionKeys[randomIndex]); + } + + // Sample from distribution + let rand = Math.random() * sumN; + for (let i = 0; i < actionKeys.length; i++) { + rand -= nPowered[i]; + if (rand <= 0) { + return this.decodeAction(actionKeys[i]); + } + } + + // Fallback (shouldn't reach here due to floating point precision) + return this.decodeAction(actionKeys[actionKeys.length - 1]); + } + } + + + /** + * Compute search policy from visit counts + * π(a|s) = N(s,a)^(1/τ) / Σ_b N(s,b)^(1/τ) + * + * @param node Root node + * @param temperature Selection temperature + * @returns Normalized policy distribution + */ + private computeSearchPolicy(node: MCTSNode, temperature: number): Map { + const policy = new Map(); + const actionKeys = Array.from(node.N.keys()); + + // Compute π(a|s) ∝ N(s,a)^(1/τ) + const nPowered = actionKeys.map(key => Math.pow(node.N.get(key) ?? 0, 1 / temperature)); + const sumN = nPowered.reduce((sum, n) => sum + n, 0); + + // Handle edge case: if all visits are 0 or sum is invalid + if (!isFinite(sumN) || sumN <= 0) { + // Fallback to uniform distribution + const uniform = 1 / actionKeys.length; + for (const key of actionKeys) { + policy.set(key, uniform); + } + return policy; + } + + for (let i = 0; i < actionKeys.length; i++) { + const actionKey = actionKeys[i]; + policy.set(actionKey, nPowered[i] / sumN); + } + + return policy; + } + + + /** + * Get root value estimate (weighted average of Q-values) + */ + private getRootValue(node: MCTSNode): number { + const actionKeys = Array.from(node.N.keys()); + const totalN = Array.from(node.N.values()).reduce((sum, n) => sum + n, 0); + + if (totalN === 0) { + return 0; + } + + let weightedSum = 0; + for (const actionKey of actionKeys) { + const q = node.Q.get(actionKey) ?? 0; + const n = node.N.get(actionKey) ?? 0; + weightedSum += q * n; + } + + return weightedSum / totalN; + } + + + /** + * Add Dirichlet noise to prior probabilities at root + * P(s,a) = (1 - ε) * p_a + ε * η_a + * where η ~ Dir(α) + * + * Note: Pass move is excluded from noise to prevent exploration of + * clearly suboptimal opening passes. + */ + private addDirichletNoise(priors: Map): void { + // Exclude Pass from Dirichlet noise - it should not be explored at root + const actionKeys = Array.from(priors.keys()).filter(key => key !== "pass"); + const alpha = this.config.dirichletAlpha; + const epsilon = this.config.dirichletEpsilon; + + // If only Pass is available, no noise to add + if (actionKeys.length === 0) { + return; + } + + // Generate Dirichlet noise (simplified using Gamma distribution) + const noise: number[] = []; + let noiseSum = 0; + + for (let i = 0; i < actionKeys.length; i++) { + // Gamma(α, 1) approximation using rejection sampling + const sample = this.sampleGamma(alpha); + noise.push(sample); + noiseSum += sample; + } + + // Handle edge case: if all Gamma samples are 0 (extremely unlikely but possible) + if (!isFinite(noiseSum) || noiseSum <= 0) { + // Fallback: use uniform noise (no mixing, keep original priors) + return; + } + + // Normalize and mix with priors (only for non-Pass actions) + for (let i = 0; i < actionKeys.length; i++) { + const actionKey = actionKeys[i]; + const prior = priors.get(actionKey) ?? 0; + const noiseFraction = noise[i] / noiseSum; + priors.set(actionKey, (1 - epsilon) * prior + epsilon * noiseFraction); + } + } + + + /** + * Sample from Gamma distribution using Marsaglia and Tsang method (2000) + * Used for Dirichlet noise generation + */ + private sampleGamma(alpha: number): number { + if (alpha <= 0) { + throw new Error("Gamma distribution alpha must be > 0"); + } + + // For alpha < 1, use transformation: sample Gamma(alpha+1) then multiply by U^(1/alpha) + if (alpha < 1) { + const u = Math.random(); + const g = this.sampleGamma(alpha + 1); + return g * Math.pow(u, 1 / alpha); + } + + // For alpha >= 1, use Marsaglia and Tsang's method + const d = alpha - 1/3; + const c = 1 / Math.sqrt(9 * d); + + while (true) { + let x, v; + do { + x = this.randomNormal(); + v = 1 + c * x; + } while (v <= 0); + + v = v * v * v; + const u = Math.random(); + + // Fast acceptance check + if (u < 1 - 0.0331 * x * x * x * x) { + return d * v; + } + + // Fallback acceptance check + if (Math.log(u) < 0.5 * x * x + d * (1 - v + Math.log(v))) { + return d * v; + } + } + } + + + /** + * Sample from standard normal distribution (Box-Muller transform) + */ + private randomNormal(): number { + const u1 = Math.random(); + const u2 = Math.random(); + return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2); + } + + + /** + * Check if game state is terminal and return value if so + * + * Terminal conditions (checked in order of cost): + * 1. Game already finished (double-pass or resignation) - CHEAPEST + * 2. Board coverage > 50% AND naturally terminal (calls isNaturallyTerminal) - EXPENSIVE + * + * NOTE: The coverage check (> 50%) is an optimization to avoid expensive + * territory calculations on sparse boards where natural termination is unlikely. + * + * @param state Game state to check + * @returns Terminal value (white-positive) if terminal, null otherwise + */ + private checkTerminal(state: TrigoGame): number | null { + // 1. Check if game is already finished (double-pass, resignation, etc.) + // This is the cheapest check - just reading a status flag + if (state.getGameStatus() === "finished") { + const territory = state.getTerritory(); + return this.calculateTerminalValue(territory); + } + + // 2. Check for "natural" game end (all territory claimed, no capturing moves) + // Optimization: Only check if board is reasonably full (> 50% coverage) + // because natural termination is unlikely on sparse boards + const board = state.getBoard(); + const shape = state.getShape(); + const totalPositions = shape.x * shape.y * shape.z; + + // Count stones (cheap) + let stoneCount = 0; + + for (let x = 0; x < shape.x; x++) { + for (let y = 0; y < shape.y; y++) { + for (let z = 0; z < shape.z; z++) { + const stone = board[x][y][z]; + if (stone === 1 || stone === 2) { // StoneType.BLACK or WHITE + stoneCount++; + } + } + } + } + + const coverageRatio = stoneCount / totalPositions; + + // Only check for natural termination if board is reasonably full + if (coverageRatio > 0.5) { + if (state.isNaturallyTerminal()) { + const territory = state.getTerritory(); + return this.calculateTerminalValue(territory); + } + } + + + return null; // Not terminal + } + + + /** + * Calculate terminal value from territory scores + * Uses logarithmic scaling matching the training code + * + * @param territory Territory counts from game + * @returns Value (white-positive: positive = white winning) + */ + private calculateTerminalValue(territory: { black: number; white: number; neutral: number }): number { + const scoreDiff = territory.white - territory.black; + + if (Math.abs(scoreDiff) < 1e-6) { + // Draw/tie case + return 0.0; + } + + // Match training formula from valueCausalLoss.py:_expand_value_targets + // target = sign(score) * (1 + log(|score|)) * territory_value_factor + // The log term incentivizes winning by larger margins (logarithmically) + const territory_value_factor = 1.0; // Default from training config + const signScore = Math.sign(scoreDiff); + return signScore * (1 + Math.log(Math.abs(scoreDiff))) * territory_value_factor; + } + + + /** + * Create a new MCTS node + * + * @param state Game state (only provided for root node, null for others to save memory) + * @param parent Parent node + * @param action Action that led to this node + * @param playerToMove Player to move at this node (derived from state if available) + */ + private createNode(state: TrigoGame | null, parent: MCTSNode | null, action: Move | null, playerToMove?: number): MCTSNode { + // Determine player to move + let player: number; + if (playerToMove !== undefined) { + player = playerToMove; + } else if (state) { + // Most reliable: derive from actual game state + player = state.getCurrentPlayer(); + } else if (parent) { + // NOTE: Fallback assumes strictly alternating turns (no passes keeping same player) + // For standard Go-like games with strict alternation, this is safe. + player = parent.playerToMove === 1 ? 2 : 1; + } else { + // Default to Black for root if no info + player = 1; + } + + return { + state, + parent, + action, + N: new Map(), + W: new Map(), + Q: new Map(), + P: new Map(), + children: new Map(), + expanded: false, + terminalValue: null, + depth: parent ? parent.depth + 1 : 0, + playerToMove: player + }; + } + + + /** + * Encode move to string key for storage in maps + * Note: Only encodes position, player info is handled separately + */ + private encodeAction(move: Move): string { + if (move.isPass) { + return "pass"; + } + return `${move.x},${move.y},${move.z}`; + } + + + /** + * Decode string key back to move + * Note: Returns move with placeholder player - caller must set correct player + * based on game state before using the move externally + */ + private decodeAction(key: string): Move { + if (key === "pass") { + // Player is placeholder - will be set by caller (selectMove sets it from game state) + return { player: "black", isPass: true }; + } + + const [x, y, z] = key.split(",").map(Number); + // Player is placeholder - will be set by caller (selectMove sets it from game state) + return { player: "black", x, y, z }; + } +} diff --git a/trigo-web/inc/modelInferencer.ts b/trigo-web/inc/modelInferencer.ts index 21d3374e98ccae8ea4a7c270a35ac3ddd5e3eabf..d02d4a421d4701a13d539361f762b5ccb66df32c 100644 --- a/trigo-web/inc/modelInferencer.ts +++ b/trigo-web/inc/modelInferencer.ts @@ -6,6 +6,15 @@ * * Adapted from Node.js test_inference.js for cross-platform use * Provides causal language model inference using GPT-2 ONNX model + * + * Vocabulary Design (128 tokens): + * 0-3: Special tokens (PAD=0, START=1, END=2, VALUE=3) + * 4-7: Reserved for future use + * 10: LF (newline) for multi-line game records + * 32-127: ASCII printable characters (direct identity mapping) + * + * This design uses direct identity mapping: token_id = ascii_value + * No complex formulas needed - simple and efficient. */ /** @@ -82,15 +91,20 @@ export class ModelInferencer { private config: InferencerConfig; private TensorClass: TensorConstructor; - // TGN tokenizer: byte-level (0-255) + PAD(256) + START(257) + END(258) - private readonly PAD_TOKEN = 256; - private readonly START_TOKEN = 257; - private readonly END_TOKEN = 258; + // TGN tokenizer: Compact 128-token vocabulary with direct ASCII mapping + // 0-3: Special tokens (PAD, START, END, VALUE) + // 4-7: Reserved for future use + // 10: Newline (LF) + // 32-127: ASCII printable characters (direct identity mapping) + private readonly PAD_TOKEN = 0; + private readonly START_TOKEN = 1; + private readonly END_TOKEN = 2; + private readonly VALUE_TOKEN = 3; constructor(TensorClass: TensorConstructor, config: Partial = {}) { this.TensorClass = TensorClass; this.config = { - vocabSize: 259, + vocabSize: config.vocabSize || 128, // Allow override via config seqLen: 256, ...config }; @@ -342,6 +356,47 @@ export class ModelInferencer { } + /** + * Run value prediction inference (for evaluation mode models) + * For models exported with --evaluation-mode flag + * @param tokens - Token IDs (already includes START/END tokens and padding) + * @returns Predicted game outcome value in range [-1, 1] + */ + async runValuePrediction(tokens: number[]): Promise { + if (!this.session) { + throw new Error("Inferencer not initialized. Call setSession() first."); + } + + const seqLen = tokens.length; + + // Convert to BigInt64Array for ONNX int64 tensors + const inputIds = new BigInt64Array(seqLen); + for (let i = 0; i < seqLen; i++) { + inputIds[i] = BigInt(tokens[i]); + } + + // Create input tensor [1, seq_len] + const inputTensor = new this.TensorClass("int64", inputIds, [1, seqLen]); + + // Run inference + const results = await this.session.run({ + input_ids: inputTensor + }); + + // Extract value + // Output shape: [batch_size] = [1] + // For evaluation models, output name is "values" not "logits" + const values = results.values; + if (!values) { + throw new Error("Evaluation model did not return 'values' output. Check model export."); + } + + const predictedValue = values.data[0] as number; + + return predictedValue; + } + + /** * Compute softmax for a single position's logits * @param logits - Full logits array diff --git a/trigo-web/inc/trigo/game.ts b/trigo-web/inc/trigo/game.ts index e97cb56c0c591a8d83ea47f7980e715bfdc5f851..683e6f0a871d434a02746c72356a11cb12456b92 100644 --- a/trigo-web/inc/trigo/game.ts +++ b/trigo-web/inc/trigo/game.ts @@ -105,9 +105,10 @@ export class TrigoGame { // Last captured stones for Ko rule detection private lastCapturedPositions: Position[] | null = null; - // Territory cache - private territoryDirty: boolean = true; + // Static analysis cache (territory, capturing moves) + // Invalidated on any board state change private cachedTerritory: TerritoryResult | null = null; + private cachedCapturingMove: Map = new Map(); /** * Constructor @@ -152,13 +153,22 @@ export class TrigoGame { this.stepHistory = []; this.currentStepIndex = 0; this.lastCapturedPositions = null; - this.territoryDirty = true; - this.cachedTerritory = null; + this.invalidateAnalysisCache(); this.gameStatus = "idle"; this.gameResult = undefined; this.passCount = 0; } + + /** + * Invalidate all static analysis caches + * Called when board state changes + */ + private invalidateAnalysisCache(): void { + this.cachedTerritory = null; + this.cachedCapturingMove.clear(); + } + /** * Clone the game state (deep copy) * Creates an independent copy with all state preserved @@ -197,9 +207,8 @@ export class TrigoGame { }; } - // Territory cache will be recalculated on demand - cloned.territoryDirty = true; - cloned.cachedTerritory = null; + // Analysis cache will be recalculated on demand + cloned.invalidateAnalysisCache(); return cloned; } @@ -386,6 +395,115 @@ export class TrigoGame { return validPositions; } + + /** + * Check if any valid move can capture enemy stones + * Used by MCTS to determine if a position is truly terminal + * + * Results are cached and invalidated when board state changes. + * + * @param player - Optional player color (defaults to current player) + * @returns true if at least one valid move would capture stones + */ + hasCapturingMove(player?: Stone): boolean { + const playerColor = player ?? this.currentPlayer; + + // Check cache first + if (this.cachedCapturingMove.has(playerColor)) { + return this.cachedCapturingMove.get(playerColor)!; + } + + // Compute and cache the result + const result = this.computeHasCapturingMove(playerColor); + this.cachedCapturingMove.set(playerColor, result); + + return result; + } + + + /** + * Internal: Compute whether a player has any capturing move + */ + private computeHasCapturingMove(playerColor: Stone): boolean { + // Iterate through all board positions + for (let x = 0; x < this.shape.x; x++) { + for (let y = 0; y < this.shape.y; y++) { + for (let z = 0; z < this.shape.z; z++) { + // Skip occupied positions + if (this.board[x][y][z] !== StoneType.EMPTY) { + continue; + } + + const pos: Position = { x, y, z }; + + // Skip invalid moves (Ko, suicide) + if ( + isKoViolation( + pos, + playerColor, + this.board, + this.shape, + this.lastCapturedPositions + ) + ) { + continue; + } + + if (isSuicideMove(pos, playerColor, this.board, this.shape)) { + continue; + } + + // Check if this move would capture any stones + const capturedGroups = findCapturedGroups(pos, playerColor, this.board, this.shape); + if (capturedGroups.length > 0) { + return true; // Found a capturing move + } + } + } + } + + return false; // No capturing moves available + } + + + /** + * Check if the game has reached a natural terminal state + * + * A game is naturally terminal when: + * - All territory is claimed (neutral === 0) + * - AND neither player has any capturing moves available + * + * This is different from the "finished" status which requires double pass. + * Natural termination means the game state is completely settled and + * no further moves can meaningfully change the outcome. + * + * NOTE: This method is expensive due to territory calculation and capture move checking. + * Use coverage ratio check as a pre-filter when calling frequently. + * + * Used by: + * - MCTS agent (terminal detection for tree search) + * - Model battle (early stopping when settled) + * - Self-play games (early stopping when settled) + * + * @returns true if the game is naturally terminal, false otherwise + */ + isNaturallyTerminal(): boolean { + // Check if all territory is claimed (uses cached territory) + const territory = this.getTerritory(); + if (territory.neutral !== 0) { + return false; // Still has neutral territory - not terminal + } + + // All territory claimed - check if capturing moves are possible + // IMPORTANT: Check even if one player has no stones - they might still capture! + const blackCanCapture = this.hasCapturingMove(StoneType.BLACK); + const whiteCanCapture = this.hasCapturingMove(StoneType.WHITE); + + // Terminal only if neither player can capture + return !blackCanCapture && !whiteCanCapture; + } + + /** * Reset pass count (called when a stone is placed) */ @@ -419,8 +537,8 @@ export class TrigoGame { // Store captured positions for Ko rule this.lastCapturedPositions = capturedPositions.length > 0 ? capturedPositions : null; - // Mark territory as dirty - this.territoryDirty = true; + // Invalidate analysis cache + this.invalidateAnalysisCache(); // Reset pass count when a stone is placed this.resetPassCount(); @@ -442,7 +560,7 @@ export class TrigoGame { this.callbacks.onCapture(capturedPositions); } - if (this.territoryDirty && this.callbacks.onTerritoryChange) { + if (this.callbacks.onTerritoryChange) { this.callbacks.onTerritoryChange(this.getTerritory()); } @@ -584,8 +702,8 @@ export class TrigoGame { this.lastCapturedPositions = null; } - // Mark territory as dirty - this.territoryDirty = true; + // Invalidate analysis cache + this.invalidateAnalysisCache(); // Trigger callback if (this.callbacks.onStepBack) { @@ -631,8 +749,8 @@ export class TrigoGame { this.currentStepIndex++; this.currentPlayer = getEnemyColor(nextStep.player); // Switch to next player - // Mark territory as dirty - this.territoryDirty = true; + // Invalidate analysis cache + this.invalidateAnalysisCache(); // Trigger callback if (this.callbacks.onStepAdvance) { @@ -718,8 +836,8 @@ export class TrigoGame { // Recalculate pass count based on new history position this.recalculatePassCount(); - // Mark territory as dirty - this.territoryDirty = true; + // Invalidate analysis cache + this.invalidateAnalysisCache(); // Trigger callback based on direction if (index < oldStepIndex && this.callbacks.onStepBack) { @@ -763,10 +881,9 @@ export class TrigoGame { * Returns cached result if territory hasn't changed */ getTerritory(): TerritoryResult { - if (this.territoryDirty || !this.cachedTerritory) { + if (!this.cachedTerritory) this.cachedTerritory = calculateTerritory(this.board, this.shape); - this.territoryDirty = false; - } + return this.cachedTerritory; } @@ -842,8 +959,7 @@ export class TrigoGame { this.lastCapturedPositions = null; } - this.territoryDirty = true; - this.cachedTerritory = null; + this.invalidateAnalysisCache(); return true; } catch (error) { @@ -987,7 +1103,7 @@ export class TrigoGame { timeControl?: string; application?: string; [key: string]: string | undefined; - }): string { + }, {markResult}: {markResult?: boolean} = {}): string { const lines: string[] = []; // Add metadata tags @@ -1020,7 +1136,7 @@ export class TrigoGame { resultStr += "Resign"; } - lines.push(`[Result "${resultStr}"]`); + //lines.push(`[Result "${resultStr}"]`); } // Add board size (without quotes - parser expects unquoted board shape) @@ -1097,6 +1213,16 @@ export class TrigoGame { lines.push(currentLine); } + // Add score result if game has finished + if (markResult) { + // Calculate territory to get final score + const territory = this.getTerritory(); + const scoreDiff = territory.black - territory.white; + + // Add score comment: negative means black wins, positive means white wins + lines.push(`; ${scoreDiff > 0 ? '-' : scoreDiff < 0 ? '+' : ''}${Math.abs(scoreDiff)}`); + } + // Add empty line at the end lines.push(""); diff --git a/trigo-web/inc/trigoAgent.ts b/trigo-web/inc/trigoAgent.ts index 915f865278997a23dd959c996f615dd4da9d189c..21a3c755f4a4b5d9901f11be09f5b32be3774921 100644 --- a/trigo-web/inc/trigoAgent.ts +++ b/trigo-web/inc/trigoAgent.ts @@ -61,7 +61,9 @@ export class TrigoAgent { } /** - * Convert string to token IDs (byte-level encoding) + * Convert string to token IDs (byte-level encoding with ASCII direct mapping) + * For characters 32-127, token_id = ascii_value + * Special tokens (0-3) and newline (10) are handled by the tokenizer */ private stringToTokens(text: string): number[] { return Array.from(text).map((char) => char.charCodeAt(0)); diff --git a/trigo-web/inc/trigoEvaluationAgent.ts b/trigo-web/inc/trigoEvaluationAgent.ts new file mode 100644 index 0000000000000000000000000000000000000000..334bc7d22eb8d4cf5a431ac0c49479f6f48dc673 --- /dev/null +++ b/trigo-web/inc/trigoEvaluationAgent.ts @@ -0,0 +1,266 @@ +/** + * Trigo Evaluation Agent - AI agent using value prediction for position evaluation + * + * Uses evaluation mode ONNX model to predict game outcomes. + * The model takes a TGN sequence and returns a value in [-1, 1]: + * - Positive values favor White (second player) + * - Negative values favor Black (first player) + * - Values near ±1 indicate strong advantage + * - Values near 0 indicate balanced position + */ + +import { ModelInferencer } from "./modelInferencer"; +import { TrigoGame, StoneType } from "./trigo/game"; +import type { Move, Stone, Position } from "./trigo/types"; +import { encodeAb0yz } from "./trigo/ab0yz"; + +export interface ValuedMove { + move: Move; + value: number; // Position value in [-1, 1] after this move + notation: string; // TGN notation +} + +export interface PositionEvaluation { + value: number; // Current position value + interpretation: string; // Human-readable interpretation +} + +export class TrigoEvaluationAgent { + private inferencer: ModelInferencer; + private readonly START_TOKEN = 1; + private readonly END_TOKEN = 2; + private readonly VALUE_TOKEN = 3; + + constructor(inferencer: ModelInferencer) { + this.inferencer = inferencer; + } + + /** + * Check if agent is initialized (checks if inferencer has a session) + */ + isInitialized(): boolean { + return this.inferencer !== null && this.inferencer.isReady(); + } + + /** + * Convert Stone type to player string + */ + private stoneToPlayer(stone: Stone): "black" | "white" { + if (stone === StoneType.BLACK) return "black"; + if (stone === StoneType.WHITE) return "white"; + throw new Error(`Invalid stone type: ${stone}`); + } + + /** + * Encode a position to TGN notation + */ + private positionToTGN(pos: Position, shape: { x: number; y: number; z: number }): string { + const posArray = [pos.x, pos.y, pos.z]; + const shapeArray = [shape.x, shape.y, shape.z]; + return encodeAb0yz(posArray, shapeArray); + } + + /** + * Convert string to byte tokens (ASCII encoding) + */ + private stringToTokens(str: string): number[] { + const tokens: number[] = []; + for (let i = 0; i < str.length; i++) { + const charCode = str.charCodeAt(i); + // Only accept valid ASCII tokens (0-127) + if (charCode < 128) { + tokens.push(charCode); + } + } + return tokens; + } + + /** + * Tokenize TGN text with special tokens + * Adds START and END tokens for proper sequence formatting + */ + private tokenizeTGN(tgnText: string, maxLength: number = 256): number[] { + // Convert TGN to tokens + const contentTokens = this.stringToTokens(tgnText); + + // Add special tokens: START + content + END + const tokens = [this.START_TOKEN, ...contentTokens, this.END_TOKEN]; + + // Truncate if needed (preserve END token) + if (tokens.length > maxLength) { + return [...tokens.slice(0, maxLength - 1), this.END_TOKEN]; + } + + // Pad with PAD tokens (0) to fixed length + while (tokens.length < maxLength) { + tokens.push(0); // PAD_TOKEN + } + + return tokens; + } + + /** + * Evaluate a position by predicting the game outcome value + * @param game - The game to evaluate + * @returns Position evaluation with value and interpretation + */ + async evaluatePosition(game: TrigoGame): Promise { + if (!this.isInitialized()) { + throw new Error("Agent not initialized. Inferencer must have a session."); + } + + // Get current TGN + const tgn = game.toTGN().trim(); + + // Tokenize + const config = this.inferencer.getConfig(); + const tokens = this.tokenizeTGN(tgn, config.seqLen); + + // Run value prediction inference + const value = await this.inferencer.runValuePrediction(tokens); + + return { + value, + interpretation: this.interpretValue(value) + }; + } + + /** + * Interpret a position value to human-readable text + */ + private interpretValue(value: number): string { + if (value > 0.5) { + return `Strong advantage for White (+${value.toFixed(3)})`; + } else if (value > 0.1) { + return `Slight advantage for White (+${value.toFixed(3)})`; + } else if (value > -0.1) { + return `Balanced position (${value.toFixed(3)})`; + } else if (value > -0.5) { + return `Slight advantage for Black (${value.toFixed(3)})`; + } else { + return `Strong advantage for Black (${value.toFixed(3)})`; + } + } + + /** + * Evaluate all valid moves and return them sorted by value + * @param game - Current game state + * @returns Array of moves with their position values + */ + async evaluateMoves(game: TrigoGame): Promise { + if (!this.isInitialized()) { + throw new Error("Agent not initialized"); + } + + const currentPlayer = this.stoneToPlayer(game.getCurrentPlayer()); + + // Get all valid moves + const validMoves: Move[] = game.validMovePositions().map((pos) => ({ + x: pos.x, + y: pos.y, + z: pos.z, + player: currentPlayer + })); + validMoves.push({ player: currentPlayer, isPass: true }); // Add pass move + + if (validMoves.length === 0) { + return []; + } + + console.log(`[TrigoEvaluationAgent] Evaluating ${validMoves.length} moves...`); + + // Evaluate each move by simulating it and evaluating the resulting position + const valuedMoves: ValuedMove[] = []; + + for (const move of validMoves) { + // Clone game and apply move + const testGame = game.clone(); + let success: boolean; + + if (move.isPass) { + success = testGame.pass(); + } else if (move.x !== undefined && move.y !== undefined && move.z !== undefined) { + success = testGame.drop({ x: move.x, y: move.y, z: move.z }); + } else { + continue; // Invalid move format + } + + if (!success) { + continue; // Move failed + } + + // Evaluate the resulting position + const evaluation = await this.evaluatePosition(testGame); + + // Get move notation + const notation = move.isPass + ? "P" + : this.positionToTGN( + { x: move.x!, y: move.y!, z: move.z! }, + testGame.getShape() + ); + + // Model returns white-positive values (positive = white advantage) + // Adjust to current player perspective: positive = advantage for current player + const adjustedValue = currentPlayer === "white" ? evaluation.value : -evaluation.value; + + valuedMoves.push({ + move, + value: adjustedValue, + notation + }); + } + + // Sort by value (highest first - best for current player) + valuedMoves.sort((a, b) => b.value - a.value); + + return valuedMoves; + } + + /** + * Select the best move based on position evaluation + * @param game - Current game state + * @returns Best move or null if no moves available + */ + async selectBestMove(game: TrigoGame): Promise { + const valuedMoves = await this.evaluateMoves(game); + + if (valuedMoves.length === 0) { + return null; + } + + console.log(`[TrigoEvaluationAgent] Best move: ${valuedMoves[0].notation} (value: ${valuedMoves[0].value.toFixed(4)})`); + console.log(`[TrigoEvaluationAgent] Top 3 moves:`); + for (let i = 0; i < Math.min(3, valuedMoves.length); i++) { + console.log(` ${i + 1}. ${valuedMoves[i].notation}: ${valuedMoves[i].value.toFixed(4)}`); + } + + return valuedMoves[0].move; + } + + /** + * Get detailed evaluation report for current position + */ + async getEvaluationReport(game: TrigoGame): Promise<{ + currentValue: PositionEvaluation; + topMoves: ValuedMove[]; + moveCount: number; + }> { + const currentEval = await this.evaluatePosition(game); + const valuedMoves = await this.evaluateMoves(game); + + return { + currentValue: currentEval, + topMoves: valuedMoves.slice(0, 5), // Top 5 moves + moveCount: valuedMoves.length + }; + } + + /** + * Clean up resources + */ + destroy(): void { + // Agent doesn't own the inferencer, so just clear reference + console.log("[TrigoEvaluationAgent] Destroyed"); + } +} diff --git a/trigo-web/inc/trigoTreeAgent.ts b/trigo-web/inc/trigoTreeAgent.ts index 111fda95409884eee0dbd679b970524a165d4991..4d79aeb4e60d72664c0e0ee738c9a0dd13f2193e 100644 --- a/trigo-web/inc/trigoTreeAgent.ts +++ b/trigo-web/inc/trigoTreeAgent.ts @@ -20,6 +20,9 @@ export interface ScoredMove { export class TrigoTreeAgent { private inferencer: ModelInferencer; + // Special token constants (must match TGN tokenizer) + private readonly START_TOKEN = 1; + constructor(inferencer: ModelInferencer) { this.inferencer = inferencer; } @@ -78,6 +81,7 @@ export class TrigoTreeAgent { evaluatedIds: number[]; mask: number[]; moveToLeafPos: number[]; + parent: Array; } { type Seq = { moveIndex: number; tokens: number[] }; @@ -154,6 +158,9 @@ export class TrigoTreeAgent { } for (const r of roots) dfs(r); + // NOTE: moveToLeafPos[i] = -1 means the move has empty tokens (e.g., single-char notation) + // In this case, we use prefix logits directly for scoring (valid behavior) + // --- Build ancestor mask --- const mask = new Array(total * total).fill(0); for (let i = 0; i < total; i++) { @@ -164,7 +171,7 @@ export class TrigoTreeAgent { } } - return { evaluatedIds, mask, moveToLeafPos }; + return { evaluatedIds, mask, moveToLeafPos, parent }; } /** @@ -178,7 +185,8 @@ export class TrigoTreeAgent { prefixTokens: number[]; evaluatedIds: number[]; mask: number[]; - moveData: Array<{ move: Move; notation: string; leafPos: number; parentPos: number }>; + parent: Array; + moveData: Array<{ move: Move; notation: string; leafPos: number }>; } { // Get current TGN as prefix const currentTGN = game.toTGN().trim(); @@ -206,7 +214,7 @@ export class TrigoTreeAgent { prefix = currentTGN + " "; } - const prefixTokens = this.stringToTokens(prefix); + const prefixTokens = [this.START_TOKEN, ...this.stringToTokens(prefix)]; // Encode each move to tokens (only first 2 tokens) const shape = game.getShape(); @@ -221,6 +229,8 @@ export class TrigoTreeAgent { } // Exclude the last token + // For single-char notations, this results in empty tokens array, + // which means we use prefix logits directly for scoring const fullTokens = this.stringToTokens(notation); const tokens = fullTokens.slice(0, fullTokens.length - 1); @@ -229,38 +239,19 @@ export class TrigoTreeAgent { // Build prefix tree const tokenArrays = movesWithTokens.map((m) => m.tokens); - const { evaluatedIds, mask, moveToLeafPos } = this.buildPrefixTree(tokenArrays); + const { evaluatedIds, mask, moveToLeafPos, parent } = this.buildPrefixTree(tokenArrays); - // Build move data with leaf positions and parent positions + // Build move data with leaf positions only const moveData = movesWithTokens.map((m, index) => { const leafPos = moveToLeafPos[index]; - // Find parent position (root position for this move) - // Parent is the first token position - const firstToken = m.tokens[0]; - let parentPos = -1; - for (let i = 0; i < evaluatedIds.length; i++) { - if (evaluatedIds[i] === firstToken && i < leafPos) { - // This is a potential parent - // Check if it's in the same branch by checking mask - // If leafPos can see position i, then i might be the parent - if (mask[leafPos * evaluatedIds.length + i] === 1.0 && i !== leafPos) { - // Find the closest parent (maximum index less than leafPos that leaf can see) - if (i > parentPos) { - parentPos = i; - } - } - } - } - return { move: m.move, notation: m.notation, - leafPos, - parentPos + leafPos }; }); - return { prefixTokens, evaluatedIds, mask, moveData }; + return { prefixTokens, evaluatedIds, mask, parent, moveData }; } /** @@ -272,16 +263,19 @@ export class TrigoTreeAgent { ): { evaluatedIds: number[]; mask: number[]; - moveData: Array<{ move: Move; notation: string; leafPos: number; parentPos: number }>; + parent: Array; + moveData: Array<{ move: Move; notation: string; leafPos: number }>; } { return this.buildMoveTree(game, moves); } /** - * Select best move using tree attention - * Evaluates all valid moves in a single inference call + * Select move using tree attention with optional temperature sampling + * @param game Current game state + * @param temperature Sampling temperature (0 = greedy, higher = more random) + * @returns Selected move (position or Pass if no valid positions) */ - async selectBestMove(game: TrigoGame): Promise { + async selectMove(game: TrigoGame, temperature: number = 0): Promise { if (!this.inferencer.isReady()) { throw new Error("Inferencer not initialized"); } @@ -289,29 +283,76 @@ export class TrigoTreeAgent { // Get current player as string const currentPlayer = this.stoneToPlayer(game.getCurrentPlayer()); - // Get all valid moves + // Get all valid position moves (excluding Pass) const validMoves: Move[] = game.validMovePositions().map((pos) => ({ x: pos.x, y: pos.y, z: pos.z, player: currentPlayer })); - validMoves.push({ player: currentPlayer, isPass: true }); // Add pass move + // If no position moves available, return Pass directly if (validMoves.length === 0) { - return null; + return { player: currentPlayer, isPass: true }; } - // Score all moves using tree attention + // Score only position moves (Pass excluded from inference) const scoredMoves = await this.scoreMoves(game, validMoves); - // Return move with highest score + // Fallback to Pass if scoring fails if (scoredMoves.length === 0) { - return null; + return { player: currentPlayer, isPass: true }; + } + + // Select move based on temperature + if (temperature <= 0.01) { + // Greedy selection (use reduce to avoid mutating scoredMoves) + const best = scoredMoves.reduce((a, b) => (b.score > a.score ? b : a)); + return best.move; + } + + // Temperature sampling + return this.sampleMove(scoredMoves, temperature); + } + + /** + * Select best move using tree attention (greedy, temperature=0) + * Evaluates all valid moves in a single inference call + * Pass is excluded from model prediction - returned directly if no positions available + */ + async selectBestMove(game: TrigoGame): Promise { + return this.selectMove(game, 0); + } + + /** + * Sample a move from scored moves using temperature + */ + private sampleMove(scoredMoves: ScoredMove[], temperature: number): Move { + // Apply temperature scaling to log probabilities + const adjustedScores = scoredMoves.map((m) => m.score / temperature); + const maxScore = Math.max(...adjustedScores); + const expScores = adjustedScores.map((score) => Math.exp(score - maxScore)); + const sumExp = expScores.reduce((sum, exp) => sum + exp, 0); + + if (sumExp === 0 || !isFinite(sumExp)) { + // Fallback to uniform random + const idx = Math.floor(Math.random() * scoredMoves.length); + return scoredMoves[idx].move; } - scoredMoves.sort((a, b) => b.score - a.score); - return scoredMoves[0].move; + const probabilities = expScores.map((exp) => exp / sumExp); + + // Weighted random sampling + const random = Math.random(); + let cumulative = 0; + for (let i = 0; i < scoredMoves.length; i++) { + cumulative += probabilities[i]; + if (random <= cumulative) { + return scoredMoves[i].move; + } + } + + return scoredMoves[scoredMoves.length - 1].move; } /** @@ -323,13 +364,13 @@ export class TrigoTreeAgent { } // Build tree structure - const { prefixTokens, evaluatedIds, mask, moveData } = this.buildMoveTree(game, moves); + const { prefixTokens, evaluatedIds, mask, parent, moveData } = this.buildMoveTree(game, moves); - console.debug(`Tree structure: ${evaluatedIds.length} nodes for ${moveData.length} moves`); - console.debug(`Evaluated IDs:`, evaluatedIds.map((id) => String.fromCharCode(id)).join("")); + //console.debug(`Tree structure: ${evaluatedIds.length} nodes for ${moveData.length} moves`); + //console.debug(`Evaluated IDs:`, evaluatedIds.map((id) => String.fromCharCode(id)).join("")); //console.debug( // `Move positions:`, - // moveData.map((m) => `${m.notation}@${m.leafPos}(parent=${m.parentPos})`) + // moveData.map((m) => `${m.notation}@${m.leafPos}`) //); // Prepare inputs for evaluation @@ -343,10 +384,14 @@ export class TrigoTreeAgent { const output = await this.inferencer.runEvaluationInference(inputs); const { logits, numEvaluated } = output; - console.debug(`Inference output: ${numEvaluated} evaluated positions`); + //console.debug(`Inference output: ${numEvaluated} evaluated positions`); + //process.stdout.write("."); + + // Minimum probability threshold to avoid log(0) while preserving small probabilities + const MIN_PROB = 1e-10; // log(1e-10) ≈ -23 - // Score each move by accumulating log probabilities for all tokens in the path - // For each move, traverse the full path from root to leaf and sum log probabilities + // Score each move by accumulating log probabilities along the path + // For each move, build the path from root to leaf using parent array const scoredMoves: ScoredMove[] = []; // Cache softmax results for each output position to avoid recomputation @@ -361,46 +406,122 @@ export class TrigoTreeAgent { for (const data of moveData) { let logProb = 0; - // Reconstruct the full path from root to leaf using the mask - // The mask tells us which positions each position can attend to (ancestors) - // We need to find all positions from root (or first move token) to leaf - const leafPos = data.leafPos; - const path: number[] = [0]; - - // Build path by finding all ancestors that this leaf can see - // Start from position 0 and find all positions up to leafPos that are in the path - for (let pos = 0; pos <= leafPos; pos++) { - // Check if leaf can see this position (it's an ancestor or self) - if (mask[leafPos * evaluatedIds.length + pos] === 1) { - path.push(pos + 1); + // Special case: leafPos = -1 means empty tokens (single-char notation) + // Use prefix logits directly to predict the single character + if (data.leafPos === -1) { + const notationTokens = this.stringToTokens(data.notation); + if (notationTokens.length === 1) { + // Single-char notation: use prefix output (logits[0]) to predict it + const token = notationTokens[0]; + const probs = getSoftmax(0); // Prefix output + const prob = Math.max(probs[token], MIN_PROB); + logProb = Math.log(prob); + } else { + console.error(`Unexpected: leafPos=-1 but notation length=${notationTokens.length}`); + logProb = Math.log(MIN_PROB); } + + scoredMoves.push({ + move: data.move, + score: logProb, + notation: data.notation + }); + continue; // Skip the normal path processing } - //console.debug("path:", data.notation, "->", path); - // Now accumulate log probabilities for all transitions in the path - // For each token in the path, we need P(token[i] | context up to token[i-1]) - // The logits at output position j predict the NEXT token after position j - // So to get P(token at position i | context), we look at output from parent position - for (let i = 0; i < path.length; i++) { - const currentPos = path[i]; - const currentToken = data.notation.charCodeAt(i); + // Build path from leaf to root using parent array, then reverse + const pathReverse: number[] = []; + let pos: number | null = data.leafPos; + const visited = new Set(); + + // Safety checks: prevent infinite loops and invalid indices + while (pos !== null && pos !== undefined) { + // Check for cycles + if (visited.has(pos)) { + console.error(`Cycle detected in parent array at position ${pos}`); + break; + } - // Subsequent tokens: predicted from previous position - // The output at prevPos predicts the token at currentPos + // Check for valid index + if (pos < 0 || pos >= parent.length) { + console.error(`Invalid position ${pos}, parent array length: ${parent.length}`); + break; + } + + visited.add(pos); + pathReverse.push(pos); + pos = parent[pos]; + + // Safety limit to prevent runaway loops + if (pathReverse.length > 10000) { + console.error(`Path too long (>10000), possible infinite loop. leafPos: ${data.leafPos}`); + break; + } + } - console.assert(currentPos <= numEvaluated, `Output position ${currentPos} exceeds numEvaluated ${numEvaluated}`); + // Reverse to get root→leaf path (indices in evaluatedIds array) + const path = pathReverse.reverse(); + + // Now accumulate log probabilities for each transition in path + // TreeLM returns logits[0..m] where: + // logits[0] = output at prefix last position (n-1) → predicts evaluatedIds[0] + // logits[i] = output at position (n-1+i) → predicts evaluatedIds[i] + // + // For a parent→child transition: + // Parent: evaluatedIds[parentIdx] at input position (n+parentIdx) + // Parent output: at position (n+parentIdx), which is logits[parentIdx+1] + // Child token: evaluatedIds[childIdx] + // Probability: softmax(logits[parentIdx+1])[evaluatedIds[childIdx]] + + // Special case: root token (predicted from prefix last position) + if (path.length > 0) { + const rootPos = path[0]; + const rootToken = evaluatedIds[rootPos]; + + // Root is predicted by prefix last position output (logits[0]) + const probs = getSoftmax(0); + const prob = Math.max(probs[rootToken], MIN_PROB); // Clip to minimum + logProb += Math.log(prob); + } - if (currentPos <= numEvaluated) { - const probs = getSoftmax(currentPos); - const prob = probs[currentToken]; + // Subsequent transitions: parent→child in tree + for (let i = 1; i < path.length; i++) { + const parentPos = path[i - 1]; // evaluatedIds index + const childPos = path[i]; // evaluatedIds index + const childToken = evaluatedIds[childPos]; + + // Parent output is at logits[parentPos+1] + const logitsIndex = parentPos + 1; + + // Check bounds: logitsIndex must be <= numEvaluated + // (logits has length numEvaluated+1, indices 0 to numEvaluated) + if (logitsIndex <= numEvaluated) { + const probs = getSoftmax(logitsIndex); + const prob = Math.max(probs[childToken], MIN_PROB); // Clip to minimum + logProb += Math.log(prob); + } else { + // Parent position out of bounds + logProb += Math.log(MIN_PROB); + } + } - if (prob > 0) - logProb += Math.log(prob); - else - logProb += -100; + // CRITICAL: Add probability for the LAST token (excluded from tree) + // The last character of the move notation was excluded from evaluatedIds + // We need to predict it using the leaf node's output + if (path.length > 0) { + const leafPos = path[path.length - 1]; // Last position in path + const lastToken = this.stringToTokens(data.notation).pop()!; // Last char of notation + + // Leaf output is at logits[leafPos+1] + const logitsIndex = leafPos + 1; + + if (logitsIndex <= numEvaluated) { + const probs = getSoftmax(logitsIndex); + const prob = Math.max(probs[lastToken], MIN_PROB); // Clip to minimum + logProb += Math.log(prob); + } else { + logProb += Math.log(MIN_PROB); } - else - logProb += -100; } scoredMoves.push({ diff --git a/trigo-web/package-lock.json b/trigo-web/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..1503b2c7a98ce49266c9c4d4fab84b94777a0edc --- /dev/null +++ b/trigo-web/package-lock.json @@ -0,0 +1,4359 @@ +{ + "name": "trigo-web", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "trigo-web", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "dotenv": "^17.2.3" + }, + "devDependencies": { + "@types/node": "^24.10.0", + "@types/yargs": "^17.0.34", + "@vitejs/plugin-vue": "^5.2.4", + "@vitest/ui": "^4.0.6", + "concurrently": "^7.6.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", + "husky": "^9.1.7", + "jison": "^0.4.18", + "jsdom": "^27.1.0", + "lint-staged": "^16.2.7", + "onnxruntime-node": "^1.23.2", + "onnxruntime-web": "1.23.2", + "prettier": "^3.6.2", + "tsx": "^4.20.6", + "typescript": "^5.2.2", + "vite": "^5.4.21", + "vitest": "^4.0.6", + "vue": "^3.3.4", + "vue-tsc": "^3.1.3", + "yargs": "^18.0.0" + } + }, + "node_modules/@acemir/cssom": { + "version": "0.9.19", + "dev": true, + "license": "MIT" + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "lru-cache": "^11.2.1" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.7.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.2" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.15", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.1", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@eslint/core": "^0.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.16.0", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.38.0", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.0", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@eslint/core": "^0.16.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.5", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.5", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/node": { + "version": "24.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.34", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.6", + "@vitest/utils": "4.0.6", + "chai": "^6.0.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.6", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.6", + "magic-string": "^0.30.19", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.6", + "fflate": "^0.8.2", + "flatted": "^3.3.3", + "pathe": "^2.0.3", + "sirv": "^3.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "4.0.6" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.6", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz", + "integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.23" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz", + "integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz", + "integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.22", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.4", + "@vue/shared": "3.5.22", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-core/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.22", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.22", + "@vue/shared": "3.5.22" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.22", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.4", + "@vue/compiler-core": "3.5.22", + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.19", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.22", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.22", + "@vue/shared": "3.5.22" + } + }, + "node_modules/@vue/language-core": { + "version": "3.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.0.0", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.22", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.22" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.22", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.22", + "@vue/shared": "3.5.22" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.22", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.22", + "@vue/runtime-core": "3.5.22", + "@vue/shared": "3.5.22", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.22", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22" + }, + "peerDependencies": { + "vue": "3.5.22" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.22", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.15.0", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/alien-signals": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.1.0.tgz", + "integrity": "sha512-yufC6VpSy8tK3I0lO67pjumo5JvDQVQyr38+3OHqe6CHl1t2VZekKZ7EKKZSqk0cRmE7U7tfZbpXiKNzuc+ckg==", + "dev": true, + "license": "MIT" + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "license": "BSD-3-Clause OR MIT", + "optional": true, + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/ansi-escapes": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0", + "peer": true + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chai": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cjson": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/cjson/-/cjson-0.3.0.tgz", + "integrity": "sha512-bBRQcCIHzI1IVH59fR0bwGrFmi3Btb/JNwM/n401i1DnYgWndpsUBiQRAddLflkZage20A2d25OAWZZk0vBRlA==", + "dev": true, + "dependencies": { + "jsonlint": "1.6.0" + }, + "engines": { + "node": ">= 0.3.0" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz", + "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^7.1.0", + "string-width": "^8.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/colors": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "integrity": "sha512-XjsuUwpDeY98+yz959OlUK6m7mLBM+1MEG5oaenfuQnNnrQk1WvtcvFgN3FNDP3f2NmZ211t0mNEfSEN1h0eIg==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commander": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/concurrently": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.6.0.tgz", + "integrity": "sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.29.1", + "lodash": "^4.17.21", + "rxjs": "^7.0.0", + "shell-quote": "^1.7.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^17.3.1" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/concurrently/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/concurrently/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/concurrently/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssstyle": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^4.0.3", + "@csstools/css-syntax-patches-for-csstree": "^1.0.14", + "css-tree": "^3.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.0.tgz", + "integrity": "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/ebnf-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/ebnf-parser/-/ebnf-parser-0.1.10.tgz", + "integrity": "sha512-urvSxVQ6XJcoTpc+/x2pWhhuOX4aljCNQpwzw+ifZvV1andZkAmiJc3Rq1oGEAQmcjiLceyMXOy1l8ms8qs2fQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz", + "integrity": "sha512-z9FWgKc48wjMlpzF5ymKS1AF8OIgnKLp9VyN7KbdtyrP/9lndwUFqCtMm+TAJmJf7KJFFYc4cFJfVTTGkKEwsA==", + "dev": true, + "dependencies": { + "esprima": "~1.1.1", + "estraverse": "~1.5.0", + "esutils": "~1.0.0" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.10.0" + }, + "optionalDependencies": { + "source-map": "~0.1.33" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", + "integrity": "sha512-FpCjJDfmo3vsc/1zKSeqR5k42tcIhxFIlvq+h9j0fO2q/h2uLKyweq7rYJ+0CoVvrGQOxIS5wyBrW/+vF58BUQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/escodegen/node_modules/esutils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", + "integrity": "sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint": { + "version": "9.38.0", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.1", + "@eslint/core": "^0.16.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.38.0", + "@eslint/plugin-kit": "^0.4.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz", + "integrity": "sha512-qxxB994/7NtERxgXdFgLHIs9M6bhLXc6qtUmWZ3L8+gTQ9qaoyki2887P2IqAYsoENyr8SUbTutStDniOHSDHg==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatbuffers": { + "version": "25.9.23", + "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-25.9.23.tgz", + "integrity": "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/guid-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", + "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/jison": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/jison/-/jison-0.4.18.tgz", + "integrity": "sha512-FKkCiJvozgC7VTHhMJ00a0/IApSxhlGsFIshLW6trWJ8ONX2TQJBBz6DlcO1Gffy4w9LT+uL+PA+CVnUSJMF7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cjson": "0.3.0", + "ebnf-parser": "0.1.10", + "escodegen": "1.3.x", + "esprima": "1.1.x", + "jison-lex": "0.3.x", + "JSONSelect": "0.4.0", + "lex-parser": "~0.1.3", + "nomnom": "1.5.2" + }, + "bin": { + "jison": "lib/cli.js" + }, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/jison-lex": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/jison-lex/-/jison-lex-0.3.4.tgz", + "integrity": "sha512-EBh5wrXhls1cUwROd5DcDHR1sG7CdsCFSqY1027+YA1RGxz+BX2TDLAhdsQf40YEtFDGoiO0Qm8PpnBl2EzDJw==", + "dev": true, + "dependencies": { + "lex-parser": "0.1.x", + "nomnom": "1.5.2" + }, + "bin": { + "jison-lex": "cli.js" + }, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "27.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@acemir/cssom": "^0.9.19", + "@asamuzakjp/dom-selector": "^6.7.3", + "cssstyle": "^5.3.2", + "data-urls": "^6.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.1.0", + "ws": "^8.18.3", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, + "node_modules/jsonlint": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.0.tgz", + "integrity": "sha512-x6YLBe6NjdpmIeiklwQOxsZuYj/SOWkT33GlTpaG1UdFGjdWjPcxJ1CWZAX3wA7tarz8E2YHF6KiW5HTapPlXw==", + "dev": true, + "dependencies": { + "JSV": ">= 4.0.x", + "nomnom": ">= 1.5.x" + }, + "bin": { + "jsonlint": "lib/cli.js" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/JSONSelect": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.4.0.tgz", + "integrity": "sha512-VRLR3Su35MH+XV2lrvh9O7qWoug/TUyj9tLDjn9rtpUCNnILLrHjgd/tB0KrhugCxUpj3UqoLqfYb3fLJdIQQQ==", + "dev": true, + "engines": { + "node": ">=0.4.7" + } + }, + "node_modules/JSV": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz", + "integrity": "sha512-ZJ6wx9xaKJ3yFUhq5/sk82PJMuUyLk277I8mQeyDgCTjGdjWJIvPfaU5LIXaMuaN2UO1X3kZH4+lgphublZUHw==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lex-parser": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/lex-parser/-/lex-parser-0.1.4.tgz", + "integrity": "sha512-DuAEISsr1H4LOpmFLkyMc8YStiRWZCO8hMsoXAXSbgyfvs2WQhSt0+/FBv3ZU/JBFZMGcE+FWzEBSzwUU7U27w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lint-staged": { + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz", + "integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.2", + "listr2": "^9.0.5", + "micromatch": "^4.0.8", + "nano-spawn": "^2.0.0", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.8.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/listr2": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nano-spawn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", + "integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/nomnom": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.5.2.tgz", + "integrity": "sha512-fiVbT7BqxiQqjlR9U3FDGOSERFCKoXVCdxV2FwZuNN7/cmJ42iQx35nUFOAFDcyvemu9Adp+IlsCGlKQYLmBKw==", + "dev": true, + "dependencies": { + "colors": "0.5.x", + "underscore": "1.1.x" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onnxruntime-common": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.23.2.tgz", + "integrity": "sha512-5LFsC9Dukzp2WV6kNHYLNzp8sT6V02IubLCbzw2Xd6X5GOlr65gAX6xiJwyi2URJol/s71gaQLC5F2C25AAR2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/onnxruntime-node": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.23.2.tgz", + "integrity": "sha512-OBTsG0W8ddBVOeVVVychpVBS87A9YV5sa2hJ6lc025T97Le+J4v++PwSC4XFs1C62SWyNdof0Mh4KvnZgtt4aw==", + "dev": true, + "hasInstallScript": true, + "os": [ + "win32", + "darwin", + "linux" + ], + "dependencies": { + "adm-zip": "^0.5.16", + "global-agent": "^3.0.0", + "onnxruntime-common": "1.23.2" + } + }, + "node_modules/onnxruntime-web": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.23.2.tgz", + "integrity": "sha512-T09JUtMn+CZLk3mFwqiH0lgQf+4S7+oYHHtk6uhaYAAJI95bTcKi5bOOZYwORXfS/RLZCjDDEXGWIuOCAFlEjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatbuffers": "^25.1.24", + "guid-typescript": "^1.0.9", + "long": "^5.2.3", + "onnxruntime-common": "1.23.2", + "platform": "^1.3.6", + "protobufjs": "^7.2.4" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/rollup": { + "version": "4.52.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.5", + "@rollup/rollup-android-arm64": "4.52.5", + "@rollup/rollup-darwin-arm64": "4.52.5", + "@rollup/rollup-darwin-x64": "4.52.5", + "@rollup/rollup-freebsd-arm64": "4.52.5", + "@rollup/rollup-freebsd-x64": "4.52.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", + "@rollup/rollup-linux-arm-musleabihf": "4.52.5", + "@rollup/rollup-linux-arm64-gnu": "4.52.5", + "@rollup/rollup-linux-arm64-musl": "4.52.5", + "@rollup/rollup-linux-loong64-gnu": "4.52.5", + "@rollup/rollup-linux-ppc64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-musl": "4.52.5", + "@rollup/rollup-linux-s390x-gnu": "4.52.5", + "@rollup/rollup-linux-x64-gnu": "4.52.5", + "@rollup/rollup-linux-x64-musl": "4.52.5", + "@rollup/rollup-openharmony-arm64": "4.52.5", + "@rollup/rollup-win32-arm64-msvc": "4.52.5", + "@rollup/rollup-win32-ia32-msvc": "4.52.5", + "@rollup/rollup-win32-x64-gnu": "4.52.5", + "@rollup/rollup-win32-x64-msvc": "4.52.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT" + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ==", + "dev": true, + "optional": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "dev": true, + "license": "MIT" + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.17", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.17" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.17", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.20.6", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/underscore": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz", + "integrity": "sha512-w4QtCHoLBXw1mjofIDoMyexaEdWGMedWNDhlWTtT1V1lCRqi65Pnoygkh6+WRdr+Bm8ldkBNkNeCsXGMlQS9HQ==", + "dev": true + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitest": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.6", + "@vitest/mocker": "4.0.6", + "@vitest/pretty-format": "4.0.6", + "@vitest/runner": "4.0.6", + "@vitest/snapshot": "4.0.6", + "@vitest/spy": "4.0.6", + "@vitest/utils": "4.0.6", + "debug": "^4.4.3", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.19", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.6", + "@vitest/browser-preview": "4.0.6", + "@vitest/browser-webdriverio": "4.0.6", + "@vitest/ui": "4.0.6", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.6", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.19" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/vitest/node_modules/vite": { + "version": "7.1.12", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.22", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-sfc": "3.5.22", + "@vue/runtime-dom": "3.5.22", + "@vue/server-renderer": "3.5.22", + "@vue/shared": "3.5.22" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-tsc": { + "version": "3.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.23", + "@vue/language-core": "3.1.3" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.0.tgz", + "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", + "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/trigo-web/package.json b/trigo-web/package.json index aa644db91e3a2985c65486481fb52fe1285ad2bb..b02cc6edcfa48fb14a601f106b1817d23cfc8f03 100644 --- a/trigo-web/package.json +++ b/trigo-web/package.json @@ -48,7 +48,7 @@ "jison": "^0.4.18", "jsdom": "^27.1.0", "lint-staged": "^16.2.7", - "onnxruntime-node": "1.23.2", + "onnxruntime-node": "^1.23.2", "onnxruntime-web": "1.23.2", "prettier": "^3.6.2", "tsx": "^4.20.6", @@ -58,5 +58,8 @@ "vue": "^3.3.4", "vue-tsc": "^3.1.3", "yargs": "^18.0.0" + }, + "dependencies": { + "dotenv": "^17.2.3" } } diff --git a/trigo-web/public/onnx/20251220-trigo-value-llama-l6-h64-251220-value0.02/LlamaCausalLM_ep0036_evaluation.onnx b/trigo-web/public/onnx/20251220-trigo-value-llama-l6-h64-251220-value0.02/LlamaCausalLM_ep0036_evaluation.onnx new file mode 100644 index 0000000000000000000000000000000000000000..924fb394be0e5c573e051f5f52ee1d88e4ffecf3 --- /dev/null +++ b/trigo-web/public/onnx/20251220-trigo-value-llama-l6-h64-251220-value0.02/LlamaCausalLM_ep0036_evaluation.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f28ac6d8655eec8189887baff3677159e0c9b4dbc4c415884de9159b6d0ee621 +size 1387656 diff --git a/trigo-web/public/onnx/20251220-trigo-value-llama-l6-h64-251220-value0.02/LlamaCausalLM_ep0036_tree.onnx b/trigo-web/public/onnx/20251220-trigo-value-llama-l6-h64-251220-value0.02/LlamaCausalLM_ep0036_tree.onnx new file mode 100644 index 0000000000000000000000000000000000000000..de106907bda81d5b6c7e26ffbb04c9bd8f9d39ca --- /dev/null +++ b/trigo-web/public/onnx/20251220-trigo-value-llama-l6-h64-251220-value0.02/LlamaCausalLM_ep0036_tree.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:84f0441fa4688cd3b3b02195237a3bce8ff9380f1ba57db6fd8fe1fa9ffcec01 +size 1378983 diff --git a/trigo-web/public/onnx/20251230-trigo-value-llama-l6-h64-it2_251221-value0.01-pretrain/LlamaCausalLM_ep0036_evaluation.onnx b/trigo-web/public/onnx/20251230-trigo-value-llama-l6-h64-it2_251221-value0.01-pretrain/LlamaCausalLM_ep0036_evaluation.onnx new file mode 100644 index 0000000000000000000000000000000000000000..b946692c1601a6be1e6f05db14b288ea9d8f9a2f --- /dev/null +++ b/trigo-web/public/onnx/20251230-trigo-value-llama-l6-h64-it2_251221-value0.01-pretrain/LlamaCausalLM_ep0036_evaluation.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27cc45ccdfca3019e6561918bcbb7376985f31388b66370d029f73064a2cc93a +size 1387656 diff --git a/trigo-web/public/onnx/20251230-trigo-value-llama-l6-h64-it2_251221-value0.01-pretrain/LlamaCausalLM_ep0036_tree.onnx b/trigo-web/public/onnx/20251230-trigo-value-llama-l6-h64-it2_251221-value0.01-pretrain/LlamaCausalLM_ep0036_tree.onnx new file mode 100644 index 0000000000000000000000000000000000000000..80dd7716f225b125f5252358785bdaaa3d3f663e --- /dev/null +++ b/trigo-web/public/onnx/20251230-trigo-value-llama-l6-h64-it2_251221-value0.01-pretrain/LlamaCausalLM_ep0036_tree.onnx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:258d947a1c40578a0fc6b8a98f16965aacceed74e3c7516c697aa6f9d772bf43 +size 1378983 diff --git a/trigo-web/public/onnx/GPT2CausalLM_ep0015_evaluation.onnx b/trigo-web/public/onnx/GPT2CausalLM_ep0015_evaluation.onnx deleted file mode 100644 index 104e2f9456e93d2859162553e2656bbd1a6b67c5..0000000000000000000000000000000000000000 --- a/trigo-web/public/onnx/GPT2CausalLM_ep0015_evaluation.onnx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:91e47b22b07bc5bef2083637dabd6d51e3c9f2faf84fdaa808f5ab3afaf769f8 -size 3676431 diff --git a/trigo-web/tools/analyzeLatestGame.ts b/trigo-web/tools/analyzeLatestGame.ts new file mode 100644 index 0000000000000000000000000000000000000000..7513b60f2ba809505901568acfa0196270eee30f --- /dev/null +++ b/trigo-web/tools/analyzeLatestGame.ts @@ -0,0 +1,99 @@ +/** + * Replay and analyze the latest 5x1x1 game + */ + +import { TrigoGame } from "../inc/trigo/game.js"; + +function analyzeGame() { + console.log("================================================================================"); + console.log("Analyzing Game: game_e0ceb960fe741704.tgn"); + console.log("================================================================================\n"); + + const game = new TrigoGame({ x: 5, y: 1, z: 1 }); + game.startGame(); + + console.log("Board positions: a(0) - b(1) - 0(2) - y(3) - z(4)\n"); + + // Move 1: a 0 + console.log("Move 1a: Black plays a(0)"); + game.drop({ x: 0, y: 0, z: 0 }); + console.log(" Board:", game.getBoard().flat().flat()); + console.log(" Territory:", game.getTerritory()); + console.log(" Game status:", game.getGameStatus()); + + console.log("\nMove 1b: White plays 0(2)"); + game.drop({ x: 2, y: 0, z: 0 }); + console.log(" Board:", game.getBoard().flat().flat()); + console.log(" Territory:", game.getTerritory()); + console.log(" Valid moves for Black:", game.validMovePositions()); + + // Move 2: y z + console.log("\nMove 2a: Black plays y(3)"); + game.drop({ x: 3, y: 0, z: 0 }); + console.log(" Board:", game.getBoard().flat().flat()); + console.log(" Territory:", game.getTerritory()); + + console.log("\nMove 2b: White plays z(4)"); + game.drop({ x: 4, y: 0, z: 0 }); + console.log(" Board:", game.getBoard().flat().flat()); + console.log(" Territory:", game.getTerritory()); + + // Move 3: Pass y + console.log("\nMove 3a: Black Pass"); + game.pass(); + console.log(" Board:", game.getBoard().flat().flat()); + console.log(" Territory:", game.getTerritory()); + console.log(" Game status:", game.getGameStatus()); + + console.log("\nMove 3b: White plays y(3)"); + try { + game.drop({ x: 3, y: 0, z: 0 }); + console.log(" Board:", game.getBoard().flat().flat()); + console.log(" Territory:", game.getTerritory()); + } catch (error: any) { + console.log(" ERROR:", error.message); + } + + // Move 4: b + console.log("\nMove 4a: Black plays b(1)"); + try { + game.drop({ x: 1, y: 0, z: 0 }); + console.log(" Board:", game.getBoard().flat().flat()); + console.log(" Territory:", game.getTerritory()); + console.log(" Game status:", game.getGameStatus()); + } catch (error: any) { + console.log(" ERROR:", error.message); + } + + // Check if game should be terminal + console.log("\n" + "=".repeat(80)); + console.log("Final Analysis:"); + console.log("=".repeat(80)); + + const territory = game.getTerritory(); + console.log("Territory:", territory); + console.log("Game status:", game.getGameStatus()); + + console.log("\nChecking capture possibilities:"); + const blackCanCapture = game.hasCapturingMove(1); + const whiteCanCapture = game.hasCapturingMove(2); + console.log(" Black can capture?", blackCanCapture); + console.log(" White can capture?", whiteCanCapture); + + console.log("\nValid moves:"); + console.log(" White:", game.validMovePositions()); + + if (territory.neutral === 0 && !blackCanCapture && !whiteCanCapture) { + console.log("\n✅ Game is truly terminal (no neutral territory, no captures possible)"); + } else { + console.log("\n⚠️ Game should NOT be terminal yet!"); + if (territory.neutral > 0) { + console.log(" Reason: Still has neutral territory"); + } + if (blackCanCapture || whiteCanCapture) { + console.log(" Reason: Capture moves still available"); + } + } +} + +analyzeGame(); diff --git a/trigo-web/tools/evaluateTgnFiles.ts b/trigo-web/tools/evaluateTgnFiles.ts new file mode 100644 index 0000000000000000000000000000000000000000..770f79a75ad8a5d93e05b864067f3d5c2b91bb9e --- /dev/null +++ b/trigo-web/tools/evaluateTgnFiles.ts @@ -0,0 +1,551 @@ +/** + * TGN File Evaluation Tool + * + * This script evaluates TGN files using the value prediction model. + * It cleans TGN content by removing trailing comments, Pass moves, and spaces, + * then evaluates the position value using TrigoEvaluationAgent. + * + * Features: + * - Batch evaluation of all .tgn files in a directory + * - TGN cleaning (remove trailing comments, Pass moves, trim spaces) + * - Position value prediction in range [-1, 1] + * - Output results to console and optional JSON file + * + * Usage: + * npx tsx tools/evaluateTgnFiles.ts [options] + * + * Options: + * --model Path to evaluation mode ONNX model (default: from ONNX_EVALUATION_MODEL env var) + * --input Directory containing TGN files or single TGN file (default: ./tools/output/selfplay) + * --output Optional JSON output file for results + * --verbose Enable verbose logging + * --help Show this help message + * + * Examples: + * # Evaluate all TGN files in directory (uses model from .env) + * npx tsx tools/evaluateTgnFiles.ts --input ./tools/output/selfplay + * + * # Evaluate with specific model + * npx tsx tools/evaluateTgnFiles.ts --model ./public/onnx/model.onnx --input ./tools/output/selfplay + * + * # Save results to JSON file + * npx tsx tools/evaluateTgnFiles.ts --model ./public/onnx/model.onnx --input ./games --output ./results.json + */ + +import * as ort from "onnxruntime-node"; +import * as path from "path"; +import * as fs from "fs"; +import { fileURLToPath } from "url"; +import { TrigoGame } from "../inc/trigo/game"; +import { ModelInferencer } from "../inc/modelInferencer"; +import { TrigoEvaluationAgent } from "../inc/trigoEvaluationAgent"; +import { initializeParsers } from "../inc/trigo/parserInit"; +import { loadEnvConfig, getOnnxModelPaths, getAbsoluteModelPath, getOnnxSessionOptions } from "../inc/config"; + + +// ES module equivalent of __dirname +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Load environment variables +await loadEnvConfig(); + +// Default model paths from environment +const defaultModelPaths = getOnnxModelPaths(); + + +// Configuration +interface EvaluationConfig { + modelPath: string; + inputPath: string; + outputPath?: string; + verbose: boolean; + vocabSize: number; + seqLen: number; +} + + +// Evaluation result for a single TGN file +interface TgnEvaluationResult { + filename: string; + filepath: string; + value: number; + interpretation: string; + moveCount: number; + boardShape: string; + cleanedTgn: string; + commentValue?: number; // Value from tail comment + signMatch?: boolean; // Whether model and comment signs match + error?: string; +} + + +/** + * Parse command line arguments + */ +function parseArgs(): EvaluationConfig { + const args = process.argv.slice(2); + const config: EvaluationConfig = { + modelPath: getAbsoluteModelPath(defaultModelPaths.evaluationModel), + inputPath: path.join(__dirname, "output/selfplay"), + verbose: false, + vocabSize: 128, + seqLen: 256 + }; + + for (let i = 0; i < args.length; i++) { + switch (args[i]) { + case "--model": + config.modelPath = args[++i]; + break; + case "--input": + config.inputPath = args[++i]; + break; + case "--output": + config.outputPath = args[++i]; + break; + case "--verbose": + config.verbose = true; + break; + case "--help": + printHelp(); + process.exit(0); + default: + if (args[i].startsWith("--")) { + console.error(`Unknown option: ${args[i]}`); + printHelp(); + process.exit(1); + } + } + } + + // Validate model path (now optional since we have default) + if (!config.modelPath) { + console.error("Error: --model argument is required or set ONNX_EVALUATION_MODEL env variable"); + printHelp(); + process.exit(1); + } + + if (!fs.existsSync(config.modelPath)) { + console.error(`Error: Model file not found: ${config.modelPath}`); + process.exit(1); + } + + if (!fs.existsSync(config.inputPath)) { + console.error(`Error: Input path not found: ${config.inputPath}`); + process.exit(1); + } + + return config; +} + + +/** + * Print help message + */ +function printHelp(): void { + console.log(` +Usage: npx tsx tools/evaluateTgnFiles.ts [options] + +Options: + --model Path to evaluation mode ONNX model (default: from ONNX_EVALUATION_MODEL env var) + --input Directory containing TGN files or single TGN file (default: ./tools/output/selfplay) + --output Optional JSON output file for results + --verbose Enable verbose logging + --help Show this help message + +Examples: + # Evaluate all TGN files in directory (uses model from .env) + npx tsx tools/evaluateTgnFiles.ts --input ./tools/output/selfplay + + # Evaluate with specific model + npx tsx tools/evaluateTgnFiles.ts --model ./public/onnx/model.onnx --input ./tools/output/selfplay + + # Save results to JSON file + npx tsx tools/evaluateTgnFiles.ts --model ./public/onnx/model.onnx --input ./games --output ./results.json +`); +} + + +/** + * Extract numeric value from tail comment before cleaning + * Returns undefined if no valid numeric comment found + */ +function extractCommentValue(tgnContent: string): number | undefined { + const lines = tgnContent.split('\n'); + + // Look for comment lines at the end (lines starting with ';') + for (let i = lines.length - 1; i >= 0; i--) { + const line = lines[i].trim(); + + // Skip empty lines + if (line === '') continue; + + // Check if it's a comment line + if (line.startsWith(';')) { + // Extract numeric value from comment + // Format: "; -16" or "; 10" etc. + const match = line.match(/;\s*([+-]?\d+(?:\.\d+)?)/); + if (match) { + return parseFloat(match[1]); + } + } else { + // Stop at first non-empty, non-comment line + break; + } + } + + return undefined; +} + + +/** + * Clean TGN content by removing trailing comments, Pass moves, and spaces + * Matches the behavior of test_evaluation_mode.js + */ +function cleanTGN(tgnContent: string): string { + let lines = tgnContent.split('\n'); + + // Remove trailing empty lines and comments (lines starting with ';') + while (lines.length > 0) { + const lastLine = lines[lines.length - 1].trim(); + if (lastLine === '' || lastLine.startsWith(';')) { + lines.pop(); + } else { + break; + } + } + + // Remove trailing Pass moves + while (lines.length > 0) { + const lastLine = lines[lines.length - 1].trim(); + // Check for Pass move: "P" or ending with " P" + if (lastLine === 'P' || lastLine.endsWith(' P')) { + lines.pop(); + } else { + break; + } + } + + // Join lines and trim trailing spaces + return lines.join('\n').trim(); +} + + +/** + * Initialize the evaluation agent + */ +async function initializeAgent(config: EvaluationConfig): Promise { + console.log("Initializing Evaluation Agent..."); + console.log(` Model: ${config.modelPath}`); + console.log(` Vocab Size: ${config.vocabSize}`); + console.log(` Sequence Length: ${config.seqLen}`); + + const sessionOptions = getOnnxSessionOptions(); + const session = await ort.InferenceSession.create(config.modelPath, sessionOptions); + + const inferencer = new ModelInferencer(ort.Tensor as any, { + vocabSize: config.vocabSize, + seqLen: config.seqLen, + modelPath: config.modelPath + }); + inferencer.setSession(session as any); + + const agent = new TrigoEvaluationAgent(inferencer); + console.log("✓ Agent initialized\n"); + + return agent; +} + + +/** + * Evaluate a single TGN file + */ +async function evaluateTgnFile( + agent: TrigoEvaluationAgent, + filepath: string, + config: EvaluationConfig +): Promise { + const filename = path.basename(filepath); + + try { + // Read TGN file + const rawContent = fs.readFileSync(filepath, "utf-8"); + + // Extract comment value BEFORE cleaning + const commentValue = extractCommentValue(rawContent); + + // Clean TGN content + const cleanedTgn = cleanTGN(rawContent); + + if (config.verbose) { + console.log(`\nProcessing: ${filename}`); + console.log(` Raw length: ${rawContent.length} chars`); + console.log(` Cleaned length: ${cleanedTgn.length} chars`); + if (commentValue !== undefined) { + console.log(` Comment value: ${commentValue}`); + } + } + + // Parse TGN to create game + const game = TrigoGame.fromTGN(cleanedTgn); + + if (config.verbose) { + console.log(` Game type: ${typeof game}`); + console.log(` Is TrigoGame: ${game instanceof TrigoGame}`); + console.log(` Has getHistory: ${typeof game.getHistory}`); + } + + // Get board info + const boardShape = game.getShape(); + const boardShapeStr = `${boardShape.x}×${boardShape.y}×${boardShape.z}`; + const moveCount = game.getHistory().length; + + if (config.verbose) { + console.log(` Board: ${boardShapeStr}`); + console.log(` Moves: ${moveCount}`); + } + + // Evaluate position + const evaluation = await agent.evaluatePosition(game); + + // Compare signs if comment value exists + let signMatch: boolean | undefined = undefined; + if (commentValue !== undefined) { + // Note: There appears to be a sign inconsistency between model output and training data + const modelSign = Math.sign(evaluation.value); + const commentSign = Math.sign(commentValue); + signMatch = modelSign === commentSign; + } + + if (config.verbose) { + console.log(` Value: ${evaluation.value.toFixed(4)}`); + console.log(` ${evaluation.interpretation}`); + if (commentValue !== undefined && signMatch !== undefined) { + console.log(` Comment: ${commentValue}, Sign match: ${signMatch ? '✓' : '✗'}`); + } + } + + return { + filename, + filepath, + value: evaluation.value, + interpretation: evaluation.interpretation, + moveCount, + boardShape: boardShapeStr, + cleanedTgn, + commentValue, + signMatch + }; + + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + console.error(`✗ Error evaluating ${filename}: ${errorMessage}`); + + return { + filename, + filepath, + value: 0, + interpretation: "Error", + moveCount: 0, + boardShape: "Unknown", + cleanedTgn: "", + error: errorMessage + }; + } +} + + +/** + * Get list of TGN files to evaluate + */ +function getTgnFiles(inputPath: string): string[] { + const stats = fs.statSync(inputPath); + + if (stats.isFile()) { + // Single file + if (!inputPath.endsWith('.tgn')) { + console.error(`Error: Input file must be a .tgn file: ${inputPath}`); + process.exit(1); + } + return [inputPath]; + } else if (stats.isDirectory()) { + // Directory - get all .tgn files + const files = fs.readdirSync(inputPath) + .filter(file => file.endsWith('.tgn')) + .map(file => path.join(inputPath, file)); + + if (files.length === 0) { + console.error(`Error: No .tgn files found in directory: ${inputPath}`); + process.exit(1); + } + + return files; + } else { + console.error(`Error: Invalid input path: ${inputPath}`); + process.exit(1); + } +} + + +/** + * Main evaluation function + */ +async function evaluateDataset(config: EvaluationConfig): Promise { + console.log("=".repeat(80)); + console.log("TGN File Evaluation Tool"); + console.log("=".repeat(80)); + console.log(`Configuration:`); + console.log(` Model: ${config.modelPath}`); + console.log(` Input: ${config.inputPath}`); + if (config.outputPath) { + console.log(` Output: ${config.outputPath}`); + } + console.log(` Verbose: ${config.verbose}`); + console.log(); + + // Initialize TGN parser + console.log("Initializing TGN parser..."); + await initializeParsers(); + console.log("✓ Parser initialized\n"); + + // Get list of TGN files + const tgnFiles = getTgnFiles(config.inputPath); + console.log(`Found ${tgnFiles.length} TGN file(s) to evaluate\n`); + + // Initialize agent + const agent = await initializeAgent(config); + + // Evaluate all files + console.log("Evaluating files..."); + console.log("=".repeat(80)); + + const results: TgnEvaluationResult[] = []; + const startTime = Date.now(); + + for (let i = 0; i < tgnFiles.length; i++) { + const filepath = tgnFiles[i]; + const filename = path.basename(filepath); + const progress = ((i + 1) / tgnFiles.length * 100).toFixed(1); + + // Evaluate file + const result = await evaluateTgnFile(agent, filepath, config); + results.push(result); + + // Progress update + if (!config.verbose) { + const statusIcon = result.error ? "✗" : "✓"; + const valueStr = result.error ? "ERROR" : result.value.toFixed(4); + const commentStr = result.commentValue !== undefined + ? ` (comment: ${result.commentValue})` + : ""; + const matchStr = result.signMatch !== undefined + ? (result.signMatch ? " ✓" : " ✗") + : ""; + console.log( + `[${progress}%] ${statusIcon} ${filename}: ${valueStr}${commentStr} - ${result.interpretation}${matchStr}` + ); + } + } + + const endTime = Date.now(); + const duration = endTime - startTime; + + // Calculate statistics + const successfulResults = results.filter(r => !r.error); + const failedResults = results.filter(r => r.error); + const averageValue = successfulResults.length > 0 + ? successfulResults.reduce((sum, r) => sum + r.value, 0) / successfulResults.length + : 0; + + // Calculate sign accuracy + const resultsWithComments = successfulResults.filter(r => r.signMatch !== undefined); + const correctPredictions = resultsWithComments.filter(r => r.signMatch === true).length; + const accuracy = resultsWithComments.length > 0 + ? (correctPredictions / resultsWithComments.length) * 100 + : 0; + + // Print summary + console.log("=".repeat(80)); + console.log("Evaluation Complete!"); + console.log("=".repeat(80)); + console.log(`Total files: ${results.length}`); + console.log(`Successful: ${successfulResults.length}`); + console.log(`Failed: ${failedResults.length}`); + console.log(`Average value: ${averageValue.toFixed(4)}`); + console.log(`Total time: ${(duration / 1000).toFixed(1)}s`); + console.log(`Average time per file: ${(duration / results.length).toFixed(0)}ms`); + + // Sign accuracy statistics + if (resultsWithComments.length > 0) { + console.log(`\nSign Accuracy:`); + console.log(` Files with comments: ${resultsWithComments.length}`); + console.log(` Correct predictions: ${correctPredictions}`); + console.log(` Accuracy: ${accuracy.toFixed(1)}%`); + } + + // Value distribution + if (successfulResults.length > 0) { + const blackAdvantage = successfulResults.filter(r => r.value > 0.1).length; + const balanced = successfulResults.filter(r => r.value >= -0.1 && r.value <= 0.1).length; + const whiteAdvantage = successfulResults.filter(r => r.value < -0.1).length; + + console.log(`\nValue Distribution:`); + console.log(` Black advantage (>0.1): ${blackAdvantage} (${(blackAdvantage / successfulResults.length * 100).toFixed(1)}%)`); + console.log(` Balanced (-0.1 to 0.1): ${balanced} (${(balanced / successfulResults.length * 100).toFixed(1)}%)`); + console.log(` White advantage (<-0.1): ${whiteAdvantage} (${(whiteAdvantage / successfulResults.length * 100).toFixed(1)}%)`); + } + + // Save results to JSON if output path provided + if (config.outputPath) { + const outputData = { + evaluationTime: new Date().toISOString(), + modelPath: config.modelPath, + inputPath: config.inputPath, + totalFiles: results.length, + successful: successfulResults.length, + failed: failedResults.length, + averageValue, + durationMs: duration, + signAccuracy: { + filesWithComments: resultsWithComments.length, + correctPredictions, + accuracy: accuracy.toFixed(1) + "%" + }, + results: results.map(r => ({ + filename: r.filename, + filepath: r.filepath, + value: r.value, + interpretation: r.interpretation, + moveCount: r.moveCount, + boardShape: r.boardShape, + commentValue: r.commentValue, + signMatch: r.signMatch, + error: r.error + })) + }; + + fs.writeFileSync(config.outputPath, JSON.stringify(outputData, null, 2), "utf-8"); + console.log(`\n✓ Results saved to: ${config.outputPath}`); + } + + console.log("=".repeat(80)); +} + + +/** + * Main function + */ +async function main() { + try { + const config = parseArgs(); + await evaluateDataset(config); + } catch (error) { + console.error("Error:", error); + process.exit(1); + } +} + + +// Run main function +main(); diff --git a/trigo-web/tools/modelBattle.ts b/trigo-web/tools/modelBattle.ts new file mode 100644 index 0000000000000000000000000000000000000000..cc381a467f78074afdaeb46ad1a149cf7557ad8a --- /dev/null +++ b/trigo-web/tools/modelBattle.ts @@ -0,0 +1,880 @@ +/** + * Model vs Model Battle Script + * + * This script enables different ONNX model versions to battle against each other. + * Features: + * - Fair color rotation (each model plays equal games as Black and White) + * - Fixed board size + * - Win rate statistics + * - Optional MCTS search (disabled by default) + * + * Usage: npx tsx tools/modelBattle.ts [options] + */ + +import * as ort from "onnxruntime-node"; +import * as path from "path"; +import * as fs from "fs"; +import { TrigoGame, StoneType } from "../inc/trigo/game"; +import { ModelInferencer } from "../inc/modelInferencer"; +import { TrigoTreeAgent } from "../inc/trigoTreeAgent"; +import { TrigoEvaluationAgent } from "../inc/trigoEvaluationAgent"; +import { MCTSAgent, MCTSConfig } from "../inc/mctsAgent"; +import type { Move, BoardShape } from "../inc/trigo/types"; +import { loadEnvConfig, getOnnxSessionOptions } from "../inc/config"; + + +// ============================================================================ +// Constants +// ============================================================================ + +const DEFAULT_VOCAB_SIZE = 128; +const DEFAULT_SEQ_LEN = 256; +const DEFAULT_NUM_GAMES = 10; +const DEFAULT_TEMPERATURE = 1.0; +const DEFAULT_MAX_MOVES = 300; +const DEFAULT_MCTS_SIMULATIONS = 600; +const DEFAULT_MCTS_CPUCT = 1.0; +const DEFAULT_MCTS_DIRICHLET_ALPHA = 0.03; +const DEFAULT_MCTS_DIRICHLET_EPSILON = 0.0; // Zero noise for deterministic evaluation +const DEFAULT_KOMI = 0.5; // Komi: Black compensates White (White's advantage) + + +// ============================================================================ +// Interfaces +// ============================================================================ + +interface BattleConfig { + model1Dir: string; + model2Dir: string; + model1TreePath: string; + model2TreePath: string; + model1EvalPath?: string; + model2EvalPath?: string; + boardShape: BoardShape; + numGames: number; + temperature: number; + maxMoves: number; + useMCTS: boolean; + mctsSimulations: number; + mctsCPuct: number; + mctsDirichletAlpha: number; + mctsDirichletEpsilon: number; + outputDir?: string; + verbose: boolean; + vocabSize: number; + seqLen: number; +} + + +interface BattleResult { + gameId: number; + model1AsBlack: boolean; + winner: "model1" | "model2" | "draw"; + model1Score: number; + model2Score: number; + scoreDiff: number; + moveCount: number; + duration: number; + tgn?: string; // TGN format game record +} + + +interface BattleStatistics { + totalGames: number; + model1Wins: number; + model2Wins: number; + draws: number; + model1WinRate: number; + model2WinRate: number; + model1AsBlackWins: number; + model1AsWhiteWins: number; + model2AsBlackWins: number; + model2AsWhiteWins: number; + averageGameLength: number; + averageScoreDiff: number; + totalTime: number; + results: BattleResult[]; +} + + +type Agent = TrigoTreeAgent | MCTSAgent; + + +// ============================================================================ +// Argument Parsing +// ============================================================================ + +function printUsage(): void { + console.log(` +Usage: npx tsx tools/modelBattle.ts [options] + +Required: + --model1 Model 1 base path (without suffix, e.g., ./dir/ModelName) + --model2 Model 2 base path (without suffix, e.g., ./dir/ModelName) + --board Board shape "X*Y*Z" (e.g., "5*5*5") + +Options: + --games Number of games (default: 10, must be even) + --temperature Sampling temperature (default: 1.0) + --max-moves Maximum moves per game (default: 300) + --output Output directory for results + --verbose Enable verbose logging + +MCTS Options: + --use-mcts Enable MCTS for move selection (requires evaluation model) + --mcts-simulations MCTS simulations per move (default: 600) + --mcts-cpuct PUCT exploration constant (default: 1.0) + --mcts-dirichlet-alpha Dirichlet noise alpha (default: 0.03) + --mcts-dirichlet-epsilon Dirichlet noise epsilon (default: 0.25) + --help Show this help message + +Model Path Format: + Provide the base path WITHOUT the suffix. The script auto-appends: + - _tree.onnx -> Tree model (required) + - _evaluation.onnx -> Evaluation model (auto-detected, required for MCTS) + +Examples: + # Battle two models on 5x5x5 board for 20 games + npx tsx tools/modelBattle.ts \\ + --model1 ./public/onnx/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045 \\ + --model2 ./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2LMHeadModel_ep0100 \\ + --board "5*5*5" --games 20 + + # Battle with MCTS enabled + npx tsx tools/modelBattle.ts \\ + --model1 ./public/onnx/model_v1/Model_ep0100 \\ + --model2 ./public/onnx/model_v2/Model_ep0100 \\ + --board "5*5*5" --games 20 --use-mcts --mcts-simulations 400 +`); +} + + +function parseBoardShape(shapeStr: string): BoardShape | null { + const match = shapeStr.match(/^(\d+)\*(\d+)\*(\d+)$/); + if (!match) return null; + return { + x: parseInt(match[1], 10), + y: parseInt(match[2], 10), + z: parseInt(match[3], 10) + }; +} + + +/** + * Resolve model paths from base path (without suffix) + * Auto-appends _tree.onnx and _evaluation.onnx suffixes + * Returns { treePath, evalPath } or null if tree model not found + */ +function resolveModelPaths(basePath: string): { treePath: string; evalPath?: string } | null { + const treePath = `${basePath}_tree.onnx`; + const evalPath = `${basePath}_evaluation.onnx`; + + if (!fs.existsSync(treePath)) { + console.error(`Tree model not found: ${treePath}`); + return null; + } + + return { + treePath, + evalPath: fs.existsSync(evalPath) ? evalPath : undefined + }; +} + + +/** + * Check if next argument exists and is not a flag + */ +function requireNextArg(args: string[], i: number, flag: string): string | null { + const next = args[i + 1]; + if (!next || next.startsWith("-")) { + console.error(`Error: ${flag} requires a value`); + printUsage(); + return null; + } + return next; +} + + +/** + * Parse and validate integer argument + */ +function parseIntArg(value: string, flag: string, minValue?: number): number | null { + const n = parseInt(value, 10); + if (Number.isNaN(n)) { + console.error(`Error: Invalid integer for ${flag}: ${value}`); + return null; + } + if (minValue !== undefined && n < minValue) { + console.error(`Error: ${flag} must be >= ${minValue}`); + return null; + } + return n; +} + + +/** + * Parse and validate float argument + */ +function parseFloatArg(value: string, flag: string, minValue?: number): number | null { + const n = parseFloat(value); + if (Number.isNaN(n) || !Number.isFinite(n)) { + console.error(`Error: Invalid number for ${flag}: ${value}`); + return null; + } + if (minValue !== undefined && n < minValue) { + console.error(`Error: ${flag} must be >= ${minValue}`); + return null; + } + return n; +} + + +function parseArgs(): BattleConfig | null { + const args = process.argv.slice(2); + + if (args.includes("--help") || args.includes("-h")) { + printUsage(); + return null; + } + + let model1Base = ""; + let model2Base = ""; + let boardShape: BoardShape = { x: 5, y: 5, z: 5 }; + let boardSpecified = false; + let numGames = DEFAULT_NUM_GAMES; + let temperature = DEFAULT_TEMPERATURE; + let maxMoves = DEFAULT_MAX_MOVES; + let useMCTS = false; + let mctsSimulations = DEFAULT_MCTS_SIMULATIONS; + let mctsCPuct = DEFAULT_MCTS_CPUCT; + let mctsDirichletAlpha = DEFAULT_MCTS_DIRICHLET_ALPHA; + let mctsDirichletEpsilon = DEFAULT_MCTS_DIRICHLET_EPSILON; + let outputDir: string | undefined; + let verbose = false; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + + switch (arg) { + case "--model1": { + const value = requireNextArg(args, i, "--model1"); + if (!value) return null; + model1Base = value; + i++; + break; + } + case "--model2": { + const value = requireNextArg(args, i, "--model2"); + if (!value) return null; + model2Base = value; + i++; + break; + } + case "--board": { + const value = requireNextArg(args, i, "--board"); + if (!value) return null; + const shape = parseBoardShape(value); + if (!shape) { + console.error(`Invalid board shape: ${value}. Use format "X*Y*Z" (e.g., "5*5*5")`); + return null; + } + boardShape = shape; + boardSpecified = true; + i++; + break; + } + case "--games": { + const value = requireNextArg(args, i, "--games"); + if (!value) return null; + const parsed = parseIntArg(value, "--games", 1); + if (parsed === null) return null; + numGames = parsed; + i++; + break; + } + case "--temperature": { + const value = requireNextArg(args, i, "--temperature"); + if (!value) return null; + const parsed = parseFloatArg(value, "--temperature", 0); + if (parsed === null) return null; + temperature = parsed; + i++; + break; + } + case "--max-moves": { + const value = requireNextArg(args, i, "--max-moves"); + if (!value) return null; + const parsed = parseIntArg(value, "--max-moves", 1); + if (parsed === null) return null; + maxMoves = parsed; + i++; + break; + } + case "--output": { + const value = requireNextArg(args, i, "--output"); + if (!value) return null; + outputDir = value; + i++; + break; + } + case "--verbose": + verbose = true; + break; + case "--use-mcts": + useMCTS = true; + break; + case "--mcts-simulations": { + const value = requireNextArg(args, i, "--mcts-simulations"); + if (!value) return null; + const parsed = parseIntArg(value, "--mcts-simulations", 1); + if (parsed === null) return null; + mctsSimulations = parsed; + i++; + break; + } + case "--mcts-cpuct": { + const value = requireNextArg(args, i, "--mcts-cpuct"); + if (!value) return null; + const parsed = parseFloatArg(value, "--mcts-cpuct", 0); + if (parsed === null) return null; + mctsCPuct = parsed; + i++; + break; + } + case "--mcts-dirichlet-alpha": { + const value = requireNextArg(args, i, "--mcts-dirichlet-alpha"); + if (!value) return null; + const parsed = parseFloatArg(value, "--mcts-dirichlet-alpha", 0); + if (parsed === null) return null; + mctsDirichletAlpha = parsed; + i++; + break; + } + case "--mcts-dirichlet-epsilon": { + const value = requireNextArg(args, i, "--mcts-dirichlet-epsilon"); + if (!value) return null; + const parsed = parseFloatArg(value, "--mcts-dirichlet-epsilon", 0); + if (parsed === null) return null; + mctsDirichletEpsilon = parsed; + i++; + break; + } + default: + if (arg.startsWith("-")) { + console.error(`Unknown option: ${arg}`); + printUsage(); + return null; + } + } + } + + // Validation + if (!model1Base) { + console.error("Error: --model1 is required"); + printUsage(); + return null; + } + if (!model2Base) { + console.error("Error: --model2 is required"); + printUsage(); + return null; + } + if (!boardSpecified) { + console.error("Error: --board is required"); + printUsage(); + return null; + } + + // Resolve model paths + const model1Paths = resolveModelPaths(model1Base); + if (!model1Paths) { + return null; + } + + const model2Paths = resolveModelPaths(model2Base); + if (!model2Paths) { + return null; + } + + // Ensure even number of games for fairness + if (numGames % 2 !== 0) { + numGames++; + console.log(`Note: Adjusted games to ${numGames} (must be even for fair color rotation)`); + } + + // Check MCTS requirements + if (useMCTS) { + if (!model1Paths.evalPath) { + console.error(`Error: Evaluation model not found for model1 (expected: ${model1Base}_evaluation.onnx)`); + return null; + } + if (!model2Paths.evalPath) { + console.error(`Error: Evaluation model not found for model2 (expected: ${model2Base}_evaluation.onnx)`); + return null; + } + } + + // Calculate max moves if not explicitly set (2× board size) + if (maxMoves === DEFAULT_MAX_MOVES && boardSpecified) { + const boardSize = boardShape.x * boardShape.y * boardShape.z; + maxMoves = boardSize * 2; + } + + return { + model1Dir: path.dirname(model1Base), + model2Dir: path.dirname(model2Base), + model1TreePath: model1Paths.treePath, + model2TreePath: model2Paths.treePath, + model1EvalPath: model1Paths.evalPath, + model2EvalPath: model2Paths.evalPath, + boardShape, + numGames, + temperature, + maxMoves, + useMCTS, + mctsSimulations, + mctsCPuct, + mctsDirichletAlpha, + mctsDirichletEpsilon, + outputDir, + verbose, + vocabSize: DEFAULT_VOCAB_SIZE, + seqLen: DEFAULT_SEQ_LEN + }; +} + + +// ============================================================================ +// Agent Initialization +// ============================================================================ + +async function createAgent( + treeModelPath: string, + evalModelPath: string | undefined, + config: BattleConfig, + name: string +): Promise { + console.log(` Loading ${name}...`); + + const sessionOptions = getOnnxSessionOptions(); + + // Load tree model + const treeSession = await ort.InferenceSession.create(treeModelPath, sessionOptions); + const treeInferencer = new ModelInferencer(ort.Tensor as any, { + vocabSize: config.vocabSize, + seqLen: config.seqLen, + modelPath: treeModelPath + }); + treeInferencer.setSession(treeSession as any); + const treeAgent = new TrigoTreeAgent(treeInferencer); + + if (!config.useMCTS) { + return treeAgent; + } + + // MCTS mode requires evaluation model + if (!evalModelPath) { + throw new Error(`MCTS enabled but evaluation model path is missing for ${name}`); + } + + // Load evaluation model for MCTS + const evalSession = await ort.InferenceSession.create(evalModelPath, sessionOptions); + const evalInferencer = new ModelInferencer(ort.Tensor as any, { + vocabSize: config.vocabSize, + seqLen: config.seqLen, + modelPath: evalModelPath + }); + evalInferencer.setSession(evalSession as any); + const evalAgent = new TrigoEvaluationAgent(evalInferencer); + + const mctsConfig: MCTSConfig = { + numSimulations: config.mctsSimulations, + cPuct: config.mctsCPuct, + temperature: config.temperature, + dirichletAlpha: config.mctsDirichletAlpha, + dirichletEpsilon: config.mctsDirichletEpsilon + }; + + return new MCTSAgent(treeAgent, evalAgent, mctsConfig); +} + + +async function initializeAgents(config: BattleConfig): Promise<{ agent1: Agent; agent2: Agent }> { + console.log("\nInitializing agents..."); + + const agent1 = await createAgent( + config.model1TreePath, + config.model1EvalPath, + config, + "Model 1" + ); + console.log(` ✓ Model 1 ready`); + + const agent2 = await createAgent( + config.model2TreePath, + config.model2EvalPath, + config, + "Model 2" + ); + console.log(` ✓ Model 2 ready`); + + return { agent1, agent2 }; +} + + +// ============================================================================ +// Game Playing +// ============================================================================ + +async function playBattleGame( + agent1: Agent, + agent2: Agent, + gameId: number, + config: BattleConfig, + model1AsBlack: boolean +): Promise { + const startTime = Date.now(); + const game = new TrigoGame(config.boardShape, {}); + + // Determine which agent plays which color + const blackAgent = model1AsBlack ? agent1 : agent2; + const whiteAgent = model1AsBlack ? agent2 : agent1; + + let moveCount = 0; + let consecutivePasses = 0; + const totalPositions = config.boardShape.x * config.boardShape.y * config.boardShape.z; + const coverageThreshold = Math.floor(totalPositions * 0.5); + let territoryCheckStarted = false; + + while (moveCount < config.maxMoves) { + const currentPlayer = game.getCurrentPlayer(); + const playerStr = currentPlayer === StoneType.BLACK ? "black" : "white"; + const currentAgent = currentPlayer === StoneType.BLACK ? blackAgent : whiteAgent; + + // Check if we should start territory checking + if (!territoryCheckStarted && moveCount >= coverageThreshold) { + territoryCheckStarted = true; + } + + let selectedMove: Move; + + try { + if (currentAgent instanceof MCTSAgent) { + const result = await currentAgent.selectMove(game, moveCount); + selectedMove = result.move; + } else { + // Tree agent mode - use selectMove with temperature + selectedMove = await currentAgent.selectMove(game, config.temperature); + } + } catch (err) { + if (config.verbose) { + console.error(`Error getting move: ${err}`); + } + // Pass on error + game.pass(); + consecutivePasses++; + if (consecutivePasses >= 2) break; + moveCount++; + continue; + } + + // Apply move + let success: boolean; + if (selectedMove.isPass) { + success = game.pass(); + consecutivePasses++; + } else if (selectedMove.x !== undefined && selectedMove.y !== undefined && selectedMove.z !== undefined) { + success = game.drop({ + x: selectedMove.x, + y: selectedMove.y, + z: selectedMove.z + }); + consecutivePasses = 0; + } else { + success = game.pass(); + consecutivePasses++; + } + + if (!success) { + if (config.verbose) { + console.warn(`Move failed, passing instead`); + } + game.pass(); + consecutivePasses++; + } + + moveCount++; + + // Check game end conditions + if (consecutivePasses >= 2) break; + + // Check for natural termination (all territory claimed, no capturing moves) + if (territoryCheckStarted && !selectedMove.isPass) { + if (game.isNaturallyTerminal()) { + break; + } + } + } + + // Calculate final scores with komi (Black compensates White) + const territory = game.getTerritory(); + const blackScore = territory.black; + const whiteScore = territory.white + DEFAULT_KOMI; // White gets komi + const scoreDiff = blackScore - whiteScore; // Positive = Black wins by X + + // Determine winner (with komi applied to White) + let winner: "model1" | "model2" | "draw"; + if (blackScore > whiteScore) { + winner = model1AsBlack ? "model1" : "model2"; + } else if (whiteScore > blackScore) { + winner = model1AsBlack ? "model2" : "model1"; + } else { + winner = "draw"; + } + + const duration = Date.now() - startTime; + + // Generate TGN record if output is enabled + let tgn: string | undefined; + if (config.outputDir) { + // Format: parentDir/modelName (without _tree.onnx suffix) + const formatModelName = (treePath: string) => { + const dir = path.basename(path.dirname(treePath)); + const filename = path.basename(treePath).replace(/_tree\.onnx$/, ""); + return `${dir}/${filename}`; + }; + const blackModel = model1AsBlack ? formatModelName(config.model1TreePath) : formatModelName(config.model2TreePath); + const whiteModel = model1AsBlack ? formatModelName(config.model2TreePath) : formatModelName(config.model1TreePath); + tgn = game.toTGN({ + black: blackModel, + white: whiteModel, + event: "Model Battle", + date: new Date().toISOString().split("T")[0].replace(/-/g, ".") + }, { markResult: true }); + } + + return { + gameId, + model1AsBlack, + winner, + model1Score: model1AsBlack ? blackScore : whiteScore, + model2Score: model1AsBlack ? whiteScore : blackScore, + scoreDiff, + moveCount, + duration, + tgn + }; +} + + +// ============================================================================ +// Battle Execution +// ============================================================================ + +async function runBattle( + agent1: Agent, + agent2: Agent, + config: BattleConfig, + gamesDir?: string +): Promise { + const results: BattleResult[] = []; + const startTime = Date.now(); + + console.log(`\nStarting battle: ${config.numGames} games`); + console.log("=".repeat(80)); + + // Alternating colors for fairness + for (let i = 0; i < config.numGames; i++) { + const gameId = i + 1; + const model1AsBlack = i % 2 === 0; // Odd games: M1=Black, Even games: M1=White + const colorStr = model1AsBlack ? "M1=Black" : "M1=White"; + process.stdout.write(`Game ${gameId}/${config.numGames} (${colorStr}): `); + + const result = await playBattleGame(agent1, agent2, gameId, config, model1AsBlack); + results.push(result); + + // Save TGN immediately after each game + if (gamesDir && result.tgn) { + const colorSuffix = model1AsBlack ? "M1B" : "M1W"; + const winnerSuffix = result.winner === "model1" ? "M1" : result.winner === "model2" ? "M2" : "Draw"; + const tgnFilename = `game_${String(gameId).padStart(3, "0")}_${colorSuffix}_${winnerSuffix}.tgn`; + const tgnPath = path.join(gamesDir, tgnFilename); + fs.writeFileSync(tgnPath, result.tgn); + } + + const winnerStr = result.winner === "model1" ? "M1" : result.winner === "model2" ? "M2" : "Draw"; + const scoreDiffStr = result.scoreDiff > 0 ? `B+${result.scoreDiff.toFixed(1)}` : `W+${(-result.scoreDiff).toFixed(1)}`; + console.log(`${result.moveCount} moves, ${winnerStr} wins (${scoreDiffStr}), ${(result.duration / 1000).toFixed(1)}s`); + } + + const totalTime = Date.now() - startTime; + + // Calculate statistics + const stats: BattleStatistics = { + totalGames: config.numGames, + model1Wins: results.filter((r) => r.winner === "model1").length, + model2Wins: results.filter((r) => r.winner === "model2").length, + draws: results.filter((r) => r.winner === "draw").length, + model1WinRate: 0, + model2WinRate: 0, + model1AsBlackWins: results.filter((r) => r.model1AsBlack && r.winner === "model1").length, + model1AsWhiteWins: results.filter((r) => !r.model1AsBlack && r.winner === "model1").length, + model2AsBlackWins: results.filter((r) => !r.model1AsBlack && r.winner === "model2").length, + model2AsWhiteWins: results.filter((r) => r.model1AsBlack && r.winner === "model2").length, + averageGameLength: results.reduce((sum, r) => sum + r.moveCount, 0) / results.length, + averageScoreDiff: results.reduce((sum, r) => sum + r.scoreDiff, 0) / results.length, + totalTime, + results + }; + + stats.model1WinRate = (stats.model1Wins / stats.totalGames) * 100; + stats.model2WinRate = (stats.model2Wins / stats.totalGames) * 100; + + return stats; +} + + +// ============================================================================ +// Output and Statistics +// ============================================================================ + +function printStatistics(stats: BattleStatistics, config: BattleConfig): void { + console.log("\n" + "=".repeat(80)); + console.log("Battle Results"); + console.log("=".repeat(80)); + + console.log(`\nModel 1: ${path.basename(config.model1TreePath)}`); + console.log(`Model 2: ${path.basename(config.model2TreePath)}`); + console.log(`Board: ${config.boardShape.x}×${config.boardShape.y}×${config.boardShape.z}`); + console.log(`Games: ${stats.totalGames} (${stats.totalGames / 2} as each color)`); + console.log(`Mode: ${config.useMCTS ? `MCTS (${config.mctsSimulations} simulations)` : "Tree Attention"}`); + + console.log("\n" + "-".repeat(80)); + console.log("Win Statistics:"); + console.log("-".repeat(80)); + + console.log(`\n Model 1 Wins: ${stats.model1Wins} (${stats.model1WinRate.toFixed(1)}%)`); + console.log(` - As Black: ${stats.model1AsBlackWins}`); + console.log(` - As White: ${stats.model1AsWhiteWins}`); + + console.log(`\n Model 2 Wins: ${stats.model2Wins} (${stats.model2WinRate.toFixed(1)}%)`); + console.log(` - As Black: ${stats.model2AsBlackWins}`); + console.log(` - As White: ${stats.model2AsWhiteWins}`); + + console.log(`\n Draws: ${stats.draws}`); + + console.log("\n" + "-".repeat(80)); + console.log("Game Statistics:"); + console.log("-".repeat(80)); + console.log(` Average game length: ${stats.averageGameLength.toFixed(1)} moves`); + console.log(` Average score diff: ${stats.averageScoreDiff > 0 ? "+" : ""}${stats.averageScoreDiff.toFixed(1)} (White - Black)`); + console.log(` Total time: ${(stats.totalTime / 60000).toFixed(1)} minutes`); + console.log(` Average time per game: ${(stats.totalTime / stats.totalGames / 1000).toFixed(1)} seconds`); + + console.log("\n" + "=".repeat(80)); + if (stats.model1Wins > stats.model2Wins) { + console.log(`Winner: Model 1 (${stats.model1WinRate.toFixed(1)}% vs ${stats.model2WinRate.toFixed(1)}%)`); + } else if (stats.model2Wins > stats.model1Wins) { + console.log(`Winner: Model 2 (${stats.model2WinRate.toFixed(1)}% vs ${stats.model1WinRate.toFixed(1)}%)`); + } else { + console.log(`Result: Tie (${stats.model1WinRate.toFixed(1)}% each)`); + } + console.log("=".repeat(80)); +} + + +function saveStatistics(stats: BattleStatistics, config: BattleConfig, gamesDir?: string): void { + if (!config.outputDir) return; + + // Create output directory + if (!fs.existsSync(config.outputDir)) { + fs.mkdirSync(config.outputDir, { recursive: true }); + } + + // Save battle summary JSON (use gamesDir name for consistency) + const dirName = gamesDir ? path.basename(gamesDir) : `battle_${new Date().toISOString().replace(/[:.]/g, "-")}`; + const summaryFilename = `${dirName}.json`; + const summaryFilepath = path.join(config.outputDir, summaryFilename); + + // Remove TGN from results to keep JSON clean + const resultsWithoutTgn = stats.results.map(({ tgn, ...rest }) => rest); + + const output = { + config: { + model1Tree: config.model1TreePath, + model2Tree: config.model2TreePath, + model1Eval: config.model1EvalPath, + model2Eval: config.model2EvalPath, + boardShape: config.boardShape, + numGames: config.numGames, + temperature: config.temperature, + useMCTS: config.useMCTS, + mctsSimulations: config.mctsSimulations, + komi: DEFAULT_KOMI + }, + statistics: { ...stats, results: resultsWithoutTgn }, + timestamp: new Date().toISOString() + }; + + fs.writeFileSync(summaryFilepath, JSON.stringify(output, null, 2)); + console.log(`\nResults saved to: ${summaryFilepath}`); + if (gamesDir) { + console.log(`Game records saved to: ${gamesDir}/`); + } +} + + +// ============================================================================ +// Main +// ============================================================================ + +async function main(): Promise { + // Load environment config + await loadEnvConfig(); + + // Parse arguments + const config = parseArgs(); + if (!config) { + process.exit(1); + } + + console.log("=".repeat(80)); + console.log("Model vs Model Battle"); + console.log("=".repeat(80)); + console.log(`Model 1: ${config.model1TreePath}`); + console.log(`Model 2: ${config.model2TreePath}`); + console.log(`Board: ${config.boardShape.x}×${config.boardShape.y}×${config.boardShape.z}`); + console.log(`Games: ${config.numGames}`); + console.log(`Mode: ${config.useMCTS ? "MCTS" : "Tree Attention"}`); + if (config.useMCTS) { + console.log(`MCTS Simulations: ${config.mctsSimulations}`); + } + + // Initialize agents + const { agent1, agent2 } = await initializeAgents(config); + + // Create games directory if output is enabled + let gamesDir: string | undefined; + if (config.outputDir) { + const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); + gamesDir = path.join(config.outputDir, `battle_${timestamp}`); + fs.mkdirSync(gamesDir, { recursive: true }); + } + + // Run battle + const stats = await runBattle(agent1, agent2, config, gamesDir); + + // Print results + printStatistics(stats, config); + + // Save results + if (config.outputDir) { + saveStatistics(stats, config, gamesDir); + } +} + + +main().catch((err) => { + console.error("Error:", err); + process.exit(1); +}); diff --git a/trigo-web/tools/output/.gitignore b/trigo-web/tools/output/.gitignore index e4249c202cd68c9e858ec0f3700511a967978944..a7ad710bf3672a82b5f715d977afa9577d1ee725 100644 --- a/trigo-web/tools/output/.gitignore +++ b/trigo-web/tools/output/.gitignore @@ -1 +1,2 @@ *.tgn +*_stats.json diff --git a/trigo-web/tools/output/battle_2025-12-17T07-42-33-553Z.json b/trigo-web/tools/output/battle_2025-12-17T07-42-33-553Z.json new file mode 100644 index 0000000000000000000000000000000000000000..e83d73fcbc1e29294b7bd45de7dcad96588deebd --- /dev/null +++ b/trigo-web/tools/output/battle_2025-12-17T07-42-33-553Z.json @@ -0,0 +1,1036 @@ +{ + "config": { + "model1Tree": "./public/onnx/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_tree.onnx", + "model2Tree": "./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_tree.onnx", + "model1Eval": "./public/onnx/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_evaluation.onnx", + "model2Eval": "./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_evaluation.onnx", + "boardShape": { + "x": 5, + "y": 5, + "z": 1 + }, + "numGames": 100, + "temperature": 1, + "useMCTS": false, + "mctsSimulations": 600, + "komi": 0.5 + }, + "statistics": { + "totalGames": 100, + "model1Wins": 31, + "model2Wins": 69, + "draws": 0, + "model1WinRate": 31, + "model2WinRate": 69, + "model1AsBlackWins": 13, + "model1AsWhiteWins": 18, + "model2AsBlackWins": 32, + "model2AsWhiteWins": 37, + "averageGameLength": 33.68, + "averageScoreDiff": -1.98, + "totalTime": 9992, + "results": [ + { + "gameId": 1, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 40, + "duration": 242 + }, + { + "gameId": 2, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 111 + }, + { + "gameId": 3, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 19.5, + "scoreDiff": -14.5, + "moveCount": 42, + "duration": 213 + }, + { + "gameId": 4, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 41, + "duration": 127 + }, + { + "gameId": 5, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 27, + "duration": 81 + }, + { + "gameId": 6, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 18, + "scoreDiff": 10.5, + "moveCount": 27, + "duration": 73 + }, + { + "gameId": 7, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 33, + "duration": 110 + }, + { + "gameId": 8, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 45, + "duration": 182 + }, + { + "gameId": 9, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 40, + "duration": 135 + }, + { + "gameId": 10, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 33, + "duration": 108 + }, + { + "gameId": 11, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 18.5, + "scoreDiff": -11.5, + "moveCount": 28, + "duration": 73 + }, + { + "gameId": 12, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 9, + "scoreDiff": -6.5, + "moveCount": 34, + "duration": 101 + }, + { + "gameId": 13, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 120 + }, + { + "gameId": 14, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 9, + "scoreDiff": -7.5, + "moveCount": 26, + "duration": 77 + }, + { + "gameId": 15, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 74 + }, + { + "gameId": 16, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 18, + "scoreDiff": 10.5, + "moveCount": 40, + "duration": 122 + }, + { + "gameId": 17, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 80 + }, + { + "gameId": 18, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 19, + "scoreDiff": 12.5, + "moveCount": 38, + "duration": 114 + }, + { + "gameId": 19, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 86 + }, + { + "gameId": 20, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 12, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 62 + }, + { + "gameId": 21, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 36, + "duration": 117 + }, + { + "gameId": 22, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 31, + "duration": 87 + }, + { + "gameId": 23, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 44, + "duration": 148 + }, + { + "gameId": 24, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 31, + "duration": 79 + }, + { + "gameId": 25, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 29, + "duration": 74 + }, + { + "gameId": 26, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 69 + }, + { + "gameId": 27, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 36, + "duration": 97 + }, + { + "gameId": 28, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 81 + }, + { + "gameId": 29, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 112 + }, + { + "gameId": 30, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 15, + "scoreDiff": 4.5, + "moveCount": 26, + "duration": 67 + }, + { + "gameId": 31, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 40, + "duration": 120 + }, + { + "gameId": 32, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 73 + }, + { + "gameId": 33, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 81 + }, + { + "gameId": 34, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 63 + }, + { + "gameId": 35, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 48, + "duration": 171 + }, + { + "gameId": 36, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 20, + "scoreDiff": 14.5, + "moveCount": 25, + "duration": 61 + }, + { + "gameId": 37, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 98 + }, + { + "gameId": 38, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 41, + "duration": 102 + }, + { + "gameId": 39, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 5.5, + "scoreDiff": 11.5, + "moveCount": 35, + "duration": 97 + }, + { + "gameId": 40, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 16, + "scoreDiff": 6.5, + "moveCount": 44, + "duration": 149 + }, + { + "gameId": 41, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 94 + }, + { + "gameId": 42, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 53, + "duration": 158 + }, + { + "gameId": 43, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 44, + "duration": 136 + }, + { + "gameId": 44, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 34, + "duration": 93 + }, + { + "gameId": 45, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 40, + "duration": 125 + }, + { + "gameId": 46, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 30, + "duration": 77 + }, + { + "gameId": 47, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 61 + }, + { + "gameId": 48, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 12, + "scoreDiff": -1.5, + "moveCount": 28, + "duration": 71 + }, + { + "gameId": 49, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 19.5, + "scoreDiff": -13.5, + "moveCount": 28, + "duration": 71 + }, + { + "gameId": 50, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 33, + "duration": 88 + }, + { + "gameId": 51, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 35, + "duration": 99 + }, + { + "gameId": 52, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 28, + "duration": 70 + }, + { + "gameId": 53, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 36, + "duration": 104 + }, + { + "gameId": 54, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 64 + }, + { + "gameId": 55, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 36, + "duration": 103 + }, + { + "gameId": 56, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 17, + "scoreDiff": 8.5, + "moveCount": 31, + "duration": 80 + }, + { + "gameId": 57, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 16.5, + "scoreDiff": -7.5, + "moveCount": 32, + "duration": 86 + }, + { + "gameId": 58, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 45, + "duration": 138 + }, + { + "gameId": 59, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 66 + }, + { + "gameId": 60, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 7, + "scoreDiff": -9.5, + "moveCount": 30, + "duration": 79 + }, + { + "gameId": 61, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 100 + }, + { + "gameId": 62, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 62 + }, + { + "gameId": 63, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 47, + "duration": 154 + }, + { + "gameId": 64, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 75 + }, + { + "gameId": 65, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 23, + "duration": 53 + }, + { + "gameId": 66, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 74 + }, + { + "gameId": 67, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 46, + "duration": 148 + }, + { + "gameId": 68, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 12.5, + "model2Score": 13, + "scoreDiff": 0.5, + "moveCount": 29, + "duration": 128 + }, + { + "gameId": 69, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 25, + "duration": 69 + }, + { + "gameId": 70, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 39, + "duration": 104 + }, + { + "gameId": 71, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 44, + "duration": 141 + }, + { + "gameId": 72, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 32, + "duration": 89 + }, + { + "gameId": 73, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 5.5, + "scoreDiff": -0.5, + "moveCount": 12, + "duration": 25 + }, + { + "gameId": 74, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 37, + "duration": 114 + }, + { + "gameId": 75, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 61 + }, + { + "gameId": 76, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 17, + "scoreDiff": 8.5, + "moveCount": 33, + "duration": 156 + }, + { + "gameId": 77, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 36, + "duration": 101 + }, + { + "gameId": 78, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 41, + "duration": 110 + }, + { + "gameId": 79, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 15.5, + "scoreDiff": -5.5, + "moveCount": 30, + "duration": 75 + }, + { + "gameId": 80, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 12.5, + "model2Score": 13, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 86 + }, + { + "gameId": 81, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 85 + }, + { + "gameId": 82, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 41, + "duration": 148 + }, + { + "gameId": 83, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 36, + "duration": 114 + }, + { + "gameId": 84, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 45, + "duration": 134 + }, + { + "gameId": 85, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 14.5, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 71 + }, + { + "gameId": 86, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 46, + "duration": 130 + }, + { + "gameId": 87, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 31, + "duration": 78 + }, + { + "gameId": 88, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 45, + "duration": 134 + }, + { + "gameId": 89, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 16.5, + "scoreDiff": -9.5, + "moveCount": 35, + "duration": 99 + }, + { + "gameId": 90, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 7, + "scoreDiff": -2.5, + "moveCount": 18, + "duration": 40 + }, + { + "gameId": 91, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 74 + }, + { + "gameId": 92, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 49, + "duration": 148 + }, + { + "gameId": 93, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 13.5, + "scoreDiff": -1.5, + "moveCount": 29, + "duration": 74 + }, + { + "gameId": 94, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 9, + "scoreDiff": -6.5, + "moveCount": 34, + "duration": 87 + }, + { + "gameId": 95, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 31, + "duration": 78 + }, + { + "gameId": 96, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 5, + "scoreDiff": -12.5, + "moveCount": 36, + "duration": 99 + }, + { + "gameId": 97, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 15.5, + "scoreDiff": -5.5, + "moveCount": 26, + "duration": 64 + }, + { + "gameId": 98, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 71 + }, + { + "gameId": 99, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 40, + "duration": 120 + }, + { + "gameId": 100, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 37, + "duration": 95 + } + ] + }, + "timestamp": "2025-12-17T07:42:33.560Z" +} \ No newline at end of file diff --git a/trigo-web/tools/output/battle_refactor_test/battle_2025-12-17T08-17-17-931Z.json b/trigo-web/tools/output/battle_refactor_test/battle_2025-12-17T08-17-17-931Z.json new file mode 100644 index 0000000000000000000000000000000000000000..489eefc77e462ddc49d92778af541a85dfb0b643 --- /dev/null +++ b/trigo-web/tools/output/battle_refactor_test/battle_2025-12-17T08-17-17-931Z.json @@ -0,0 +1,56 @@ +{ + "config": { + "model1Tree": "./public/onnx/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_tree.onnx", + "model2Tree": "./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_tree.onnx", + "model1Eval": "./public/onnx/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_evaluation.onnx", + "model2Eval": "./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_evaluation.onnx", + "boardShape": { + "x": 5, + "y": 5, + "z": 5 + }, + "numGames": 2, + "temperature": 1, + "useMCTS": false, + "mctsSimulations": 600, + "komi": 0.5 + }, + "statistics": { + "totalGames": 2, + "model1Wins": 0, + "model2Wins": 2, + "draws": 0, + "model1WinRate": 0, + "model2WinRate": 100, + "model1AsBlackWins": 0, + "model1AsWhiteWins": 0, + "model2AsBlackWins": 1, + "model2AsWhiteWins": 1, + "averageGameLength": 137.5, + "averageScoreDiff": -13, + "totalTime": 4856, + "results": [ + { + "gameId": 1, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 19, + "model2Score": 60.5, + "scoreDiff": -41.5, + "moveCount": 120, + "duration": 2020 + }, + { + "gameId": 2, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 53.5, + "model2Score": 69, + "scoreDiff": 15.5, + "moveCount": 155, + "duration": 2834 + } + ] + }, + "timestamp": "2025-12-17T08:17:22.788Z" +} \ No newline at end of file diff --git a/trigo-web/tools/output/battle_results/battle_2025-12-25T00-12-27-882Z.json b/trigo-web/tools/output/battle_results/battle_2025-12-25T00-12-27-882Z.json new file mode 100644 index 0000000000000000000000000000000000000000..8a096084e191b80731490c9071c0738887eabba2 --- /dev/null +++ b/trigo-web/tools/output/battle_results/battle_2025-12-25T00-12-27-882Z.json @@ -0,0 +1,1036 @@ +{ + "config": { + "model1Tree": "/home/camus/work/trigoRL/outputs/trigor/20251224-trigo-value-llama-l6-h64-it2_251221-value0.02/LlamaCausalLM_ep0036_tree.onnx", + "model2Tree": "public/onnx/20251220-trigo-value-llama-l6-h64-251220-value0.02/LlamaCausalLM_ep0036_tree.onnx", + "model1Eval": "/home/camus/work/trigoRL/outputs/trigor/20251224-trigo-value-llama-l6-h64-it2_251221-value0.02/LlamaCausalLM_ep0036_evaluation.onnx", + "model2Eval": "public/onnx/20251220-trigo-value-llama-l6-h64-251220-value0.02/LlamaCausalLM_ep0036_evaluation.onnx", + "boardShape": { + "x": 5, + "y": 5, + "z": 1 + }, + "numGames": 100, + "temperature": 1, + "useMCTS": false, + "mctsSimulations": 600, + "komi": 0.5 + }, + "statistics": { + "totalGames": 100, + "model1Wins": 42, + "model2Wins": 58, + "draws": 0, + "model1WinRate": 42, + "model2WinRate": 57.99999999999999, + "model1AsBlackWins": 20, + "model1AsWhiteWins": 22, + "model2AsBlackWins": 28, + "model2AsWhiteWins": 30, + "averageGameLength": 35.54, + "averageScoreDiff": -1.15, + "totalTime": 12138, + "results": [ + { + "gameId": 1, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 8.5, + "scoreDiff": 8.5, + "moveCount": 31, + "duration": 142 + }, + { + "gameId": 2, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 16, + "scoreDiff": 7.5, + "moveCount": 35, + "duration": 137 + }, + { + "gameId": 3, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 18.5, + "scoreDiff": -11.5, + "moveCount": 40, + "duration": 195 + }, + { + "gameId": 4, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 41, + "duration": 168 + }, + { + "gameId": 5, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 13.5, + "scoreDiff": -5.5, + "moveCount": 28, + "duration": 115 + }, + { + "gameId": 6, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 23, + "duration": 93 + }, + { + "gameId": 7, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 96 + }, + { + "gameId": 8, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 31, + "duration": 143 + }, + { + "gameId": 9, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 29, + "duration": 112 + }, + { + "gameId": 10, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 17, + "scoreDiff": 10.5, + "moveCount": 31, + "duration": 104 + }, + { + "gameId": 11, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 31, + "duration": 89 + }, + { + "gameId": 12, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 36, + "duration": 112 + }, + { + "gameId": 13, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 3.5, + "scoreDiff": 16.5, + "moveCount": 41, + "duration": 137 + }, + { + "gameId": 14, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 20, + "scoreDiff": 15.5, + "moveCount": 50, + "duration": 203 + }, + { + "gameId": 15, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 14.5, + "scoreDiff": -6.5, + "moveCount": 27, + "duration": 101 + }, + { + "gameId": 16, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 104 + }, + { + "gameId": 17, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 30, + "duration": 94 + }, + { + "gameId": 18, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 33, + "duration": 113 + }, + { + "gameId": 19, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 100 + }, + { + "gameId": 20, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 10, + "scoreDiff": -5.5, + "moveCount": 31, + "duration": 93 + }, + { + "gameId": 21, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 16.5, + "scoreDiff": -8.5, + "moveCount": 34, + "duration": 105 + }, + { + "gameId": 22, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 6, + "scoreDiff": -11.5, + "moveCount": 46, + "duration": 196 + }, + { + "gameId": 23, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 139 + }, + { + "gameId": 24, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 22.5, + "model2Score": 2, + "scoreDiff": -20.5, + "moveCount": 44, + "duration": 155 + }, + { + "gameId": 25, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 36, + "duration": 108 + }, + { + "gameId": 26, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 41, + "duration": 143 + }, + { + "gameId": 27, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 14.5, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 92 + }, + { + "gameId": 28, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 17, + "scoreDiff": 9.5, + "moveCount": 35, + "duration": 114 + }, + { + "gameId": 29, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 124 + }, + { + "gameId": 30, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 3.5, + "model2Score": 20, + "scoreDiff": 16.5, + "moveCount": 37, + "duration": 124 + }, + { + "gameId": 31, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 88 + }, + { + "gameId": 32, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 2.5, + "model2Score": 22, + "scoreDiff": 19.5, + "moveCount": 50, + "duration": 189 + }, + { + "gameId": 33, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 5.5, + "scoreDiff": 11.5, + "moveCount": 31, + "duration": 93 + }, + { + "gameId": 34, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 13, + "scoreDiff": 1.5, + "moveCount": 29, + "duration": 77 + }, + { + "gameId": 35, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 35, + "duration": 131 + }, + { + "gameId": 36, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 7, + "scoreDiff": -8.5, + "moveCount": 43, + "duration": 152 + }, + { + "gameId": 37, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 77 + }, + { + "gameId": 38, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 16, + "scoreDiff": 7.5, + "moveCount": 31, + "duration": 84 + }, + { + "gameId": 39, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 40, + "duration": 143 + }, + { + "gameId": 40, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 2.5, + "model2Score": 21, + "scoreDiff": 18.5, + "moveCount": 39, + "duration": 138 + }, + { + "gameId": 41, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 86 + }, + { + "gameId": 42, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 19, + "scoreDiff": 14.5, + "moveCount": 43, + "duration": 155 + }, + { + "gameId": 43, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 78 + }, + { + "gameId": 44, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 31, + "duration": 93 + }, + { + "gameId": 45, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 5.5, + "scoreDiff": 13.5, + "moveCount": 37, + "duration": 112 + }, + { + "gameId": 46, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 70 + }, + { + "gameId": 47, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 2, + "model2Score": 11.5, + "scoreDiff": -9.5, + "moveCount": 50, + "duration": 208 + }, + { + "gameId": 48, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 35, + "duration": 130 + }, + { + "gameId": 49, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 36, + "duration": 134 + }, + { + "gameId": 50, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 46, + "duration": 170 + }, + { + "gameId": 51, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 44, + "duration": 133 + }, + { + "gameId": 52, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 102 + }, + { + "gameId": 53, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 70 + }, + { + "gameId": 54, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 11, + "scoreDiff": -3.5, + "moveCount": 34, + "duration": 100 + }, + { + "gameId": 55, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 21, + "model2Score": 3.5, + "scoreDiff": 17.5, + "moveCount": 47, + "duration": 153 + }, + { + "gameId": 56, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 84 + }, + { + "gameId": 57, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 44, + "duration": 149 + }, + { + "gameId": 58, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 9, + "scoreDiff": -7.5, + "moveCount": 34, + "duration": 96 + }, + { + "gameId": 59, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 2, + "model2Score": 21.5, + "scoreDiff": -19.5, + "moveCount": 44, + "duration": 142 + }, + { + "gameId": 60, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 18, + "scoreDiff": 12.5, + "moveCount": 33, + "duration": 102 + }, + { + "gameId": 61, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 28, + "duration": 89 + }, + { + "gameId": 62, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 26, + "duration": 78 + }, + { + "gameId": 63, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 48, + "duration": 181 + }, + { + "gameId": 64, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 4, + "scoreDiff": -14.5, + "moveCount": 36, + "duration": 110 + }, + { + "gameId": 65, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 87 + }, + { + "gameId": 66, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 19, + "scoreDiff": 14.5, + "moveCount": 39, + "duration": 135 + }, + { + "gameId": 67, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 43, + "duration": 143 + }, + { + "gameId": 68, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 33, + "duration": 105 + }, + { + "gameId": 69, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 28, + "duration": 82 + }, + { + "gameId": 70, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 15, + "scoreDiff": 5.5, + "moveCount": 35, + "duration": 120 + }, + { + "gameId": 71, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 29, + "duration": 85 + }, + { + "gameId": 72, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 36, + "duration": 120 + }, + { + "gameId": 73, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 19.5, + "scoreDiff": -14.5, + "moveCount": 50, + "duration": 188 + }, + { + "gameId": 74, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 11, + "scoreDiff": -2.5, + "moveCount": 34, + "duration": 121 + }, + { + "gameId": 75, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 17.5, + "scoreDiff": -11.5, + "moveCount": 36, + "duration": 106 + }, + { + "gameId": 76, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 10, + "scoreDiff": 0.5, + "moveCount": 21, + "duration": 54 + }, + { + "gameId": 77, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 96 + }, + { + "gameId": 78, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 5, + "scoreDiff": -13.5, + "moveCount": 40, + "duration": 130 + }, + { + "gameId": 79, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 3, + "model2Score": 20.5, + "scoreDiff": -17.5, + "moveCount": 48, + "duration": 174 + }, + { + "gameId": 80, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 45, + "duration": 135 + }, + { + "gameId": 81, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 2, + "model2Score": 22.5, + "scoreDiff": -20.5, + "moveCount": 44, + "duration": 147 + }, + { + "gameId": 82, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 29, + "duration": 97 + }, + { + "gameId": 83, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 39, + "duration": 181 + }, + { + "gameId": 84, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 31, + "duration": 96 + }, + { + "gameId": 85, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 83 + }, + { + "gameId": 86, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 3.5, + "model2Score": 21, + "scoreDiff": 17.5, + "moveCount": 40, + "duration": 134 + }, + { + "gameId": 87, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 39, + "duration": 127 + }, + { + "gameId": 88, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 109 + }, + { + "gameId": 89, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 84 + }, + { + "gameId": 90, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 35, + "duration": 150 + }, + { + "gameId": 91, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 2, + "model2Score": 22.5, + "scoreDiff": -20.5, + "moveCount": 46, + "duration": 181 + }, + { + "gameId": 92, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 32, + "duration": 111 + }, + { + "gameId": 93, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 111 + }, + { + "gameId": 94, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 6, + "scoreDiff": -12.5, + "moveCount": 40, + "duration": 132 + }, + { + "gameId": 95, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 17.5, + "scoreDiff": -10.5, + "moveCount": 36, + "duration": 120 + }, + { + "gameId": 96, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 80 + }, + { + "gameId": 97, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 35, + "duration": 108 + }, + { + "gameId": 98, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 41, + "duration": 142 + }, + { + "gameId": 99, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 35, + "duration": 126 + }, + { + "gameId": 100, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 48, + "duration": 154 + } + ] + }, + "timestamp": "2025-12-25T00:12:40.022Z" +} \ No newline at end of file diff --git a/trigo-web/tools/output/battle_results/battle_2025-12-25T00-29-53-678Z.json b/trigo-web/tools/output/battle_results/battle_2025-12-25T00-29-53-678Z.json new file mode 100644 index 0000000000000000000000000000000000000000..8b934d39babf0157849a796a43aeafd284613a96 --- /dev/null +++ b/trigo-web/tools/output/battle_results/battle_2025-12-25T00-29-53-678Z.json @@ -0,0 +1,1036 @@ +{ + "config": { + "model1Tree": "/home/camus/work/trigoRL/outputs/trigor/20251224-trigo-value-llama-l6-h64-it2_251221-value0.02/LlamaCausalLM_ep0036_tree.onnx", + "model2Tree": "public/onnx/20251220-trigo-value-llama-l6-h64-251220-value0.02/LlamaCausalLM_ep0036_tree.onnx", + "model1Eval": "/home/camus/work/trigoRL/outputs/trigor/20251224-trigo-value-llama-l6-h64-it2_251221-value0.02/LlamaCausalLM_ep0036_evaluation.onnx", + "model2Eval": "public/onnx/20251220-trigo-value-llama-l6-h64-251220-value0.02/LlamaCausalLM_ep0036_evaluation.onnx", + "boardShape": { + "x": 5, + "y": 5, + "z": 1 + }, + "numGames": 100, + "temperature": 1, + "useMCTS": true, + "mctsSimulations": 40, + "komi": 0.5 + }, + "statistics": { + "totalGames": 100, + "model1Wins": 52, + "model2Wins": 48, + "draws": 0, + "model1WinRate": 52, + "model2WinRate": 48, + "model1AsBlackWins": 28, + "model1AsWhiteWins": 24, + "model2AsBlackWins": 26, + "model2AsWhiteWins": 22, + "averageGameLength": 29.67, + "averageScoreDiff": -2.31, + "totalTime": 1645321, + "results": [ + { + "gameId": 1, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 20057 + }, + { + "gameId": 2, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 17059 + }, + { + "gameId": 3, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 40, + "duration": 22095 + }, + { + "gameId": 4, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 32, + "duration": 18372 + }, + { + "gameId": 5, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 14491 + }, + { + "gameId": 6, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 18, + "scoreDiff": 12.5, + "moveCount": 37, + "duration": 20103 + }, + { + "gameId": 7, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 6.5, + "scoreDiff": 9.5, + "moveCount": 33, + "duration": 18340 + }, + { + "gameId": 8, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 6.5, + "model2Score": 6, + "scoreDiff": -0.5, + "moveCount": 14, + "duration": 7562 + }, + { + "gameId": 9, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 14.5, + "scoreDiff": -4.5, + "moveCount": 50, + "duration": 30864 + }, + { + "gameId": 10, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 3.5, + "model2Score": 4, + "scoreDiff": 0.5, + "moveCount": 9, + "duration": 4468 + }, + { + "gameId": 11, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 35, + "duration": 19635 + }, + { + "gameId": 12, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 5, + "scoreDiff": 0.5, + "moveCount": 11, + "duration": 5788 + }, + { + "gameId": 13, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 3, + "model2Score": 20.5, + "scoreDiff": -17.5, + "moveCount": 42, + "duration": 23340 + }, + { + "gameId": 14, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 33, + "duration": 18135 + }, + { + "gameId": 15, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 20891 + }, + { + "gameId": 16, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 14, + "scoreDiff": 2.5, + "moveCount": 33, + "duration": 17570 + }, + { + "gameId": 17, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 27, + "duration": 15238 + }, + { + "gameId": 18, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 2.5, + "model2Score": 2, + "scoreDiff": -0.5, + "moveCount": 6, + "duration": 3217 + }, + { + "gameId": 19, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 15255 + }, + { + "gameId": 20, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 5, + "scoreDiff": -12.5, + "moveCount": 34, + "duration": 19170 + }, + { + "gameId": 21, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 29, + "duration": 16034 + }, + { + "gameId": 22, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 13982 + }, + { + "gameId": 23, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 14280 + }, + { + "gameId": 24, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 41, + "duration": 22015 + }, + { + "gameId": 25, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 6.5, + "scoreDiff": 9.5, + "moveCount": 33, + "duration": 17956 + }, + { + "gameId": 26, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 13958 + }, + { + "gameId": 27, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 38, + "duration": 20960 + }, + { + "gameId": 28, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 16, + "scoreDiff": 7.5, + "moveCount": 27, + "duration": 15707 + }, + { + "gameId": 29, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 44, + "duration": 26291 + }, + { + "gameId": 30, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 15835 + }, + { + "gameId": 31, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 31, + "duration": 16586 + }, + { + "gameId": 32, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 5, + "scoreDiff": -13.5, + "moveCount": 32, + "duration": 17911 + }, + { + "gameId": 33, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 18973 + }, + { + "gameId": 34, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 4.5, + "model2Score": 4, + "scoreDiff": -0.5, + "moveCount": 9, + "duration": 4731 + }, + { + "gameId": 35, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 45, + "duration": 25991 + }, + { + "gameId": 36, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 14175 + }, + { + "gameId": 37, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 42, + "duration": 22290 + }, + { + "gameId": 38, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 4.5, + "model2Score": 4, + "scoreDiff": -0.5, + "moveCount": 11, + "duration": 5860 + }, + { + "gameId": 39, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 44, + "duration": 23441 + }, + { + "gameId": 40, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 14501 + }, + { + "gameId": 41, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 17396 + }, + { + "gameId": 42, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 15, + "scoreDiff": 5.5, + "moveCount": 33, + "duration": 17638 + }, + { + "gameId": 43, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 14606 + }, + { + "gameId": 44, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 16, + "scoreDiff": 7.5, + "moveCount": 33, + "duration": 17649 + }, + { + "gameId": 45, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 33, + "duration": 19318 + }, + { + "gameId": 46, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 15, + "scoreDiff": 4.5, + "moveCount": 29, + "duration": 15506 + }, + { + "gameId": 47, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 29, + "duration": 14476 + }, + { + "gameId": 48, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 5.5, + "model2Score": 5, + "scoreDiff": -0.5, + "moveCount": 12, + "duration": 6400 + }, + { + "gameId": 49, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 31, + "duration": 17391 + }, + { + "gameId": 50, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 32, + "duration": 17100 + }, + { + "gameId": 51, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 4, + "model2Score": 19.5, + "scoreDiff": -15.5, + "moveCount": 40, + "duration": 22724 + }, + { + "gameId": 52, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 7, + "scoreDiff": -0.5, + "moveCount": 16, + "duration": 8675 + }, + { + "gameId": 53, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 17649 + }, + { + "gameId": 54, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 14265 + }, + { + "gameId": 55, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 19016 + }, + { + "gameId": 56, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 14505 + }, + { + "gameId": 57, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 7.5, + "scoreDiff": 7.5, + "moveCount": 35, + "duration": 19036 + }, + { + "gameId": 58, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 30, + "duration": 16643 + }, + { + "gameId": 59, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 14170 + }, + { + "gameId": 60, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 15, + "scoreDiff": 4.5, + "moveCount": 33, + "duration": 17670 + }, + { + "gameId": 61, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 14097 + }, + { + "gameId": 62, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 13, + "scoreDiff": 3.5, + "moveCount": 28, + "duration": 14276 + }, + { + "gameId": 63, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 50, + "duration": 30314 + }, + { + "gameId": 64, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 18, + "scoreDiff": 11.5, + "moveCount": 39, + "duration": 21673 + }, + { + "gameId": 65, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 20301 + }, + { + "gameId": 66, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 8, + "scoreDiff": -1.5, + "moveCount": 18, + "duration": 9842 + }, + { + "gameId": 67, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 14373 + }, + { + "gameId": 68, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 32, + "duration": 17388 + }, + { + "gameId": 69, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 14061 + }, + { + "gameId": 70, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 19, + "scoreDiff": 14.5, + "moveCount": 35, + "duration": 18698 + }, + { + "gameId": 71, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 15825 + }, + { + "gameId": 72, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 24, + "duration": 13132 + }, + { + "gameId": 73, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 18.5, + "scoreDiff": -13.5, + "moveCount": 40, + "duration": 22381 + }, + { + "gameId": 74, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 6, + "scoreDiff": 0.5, + "moveCount": 13, + "duration": 6891 + }, + { + "gameId": 75, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 31, + "duration": 16670 + }, + { + "gameId": 76, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 50, + "duration": 30242 + }, + { + "gameId": 77, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 17868 + }, + { + "gameId": 78, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 14, + "scoreDiff": 5.5, + "moveCount": 25, + "duration": 13593 + }, + { + "gameId": 79, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 14742 + }, + { + "gameId": 80, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 33, + "duration": 17821 + }, + { + "gameId": 81, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 18617 + }, + { + "gameId": 82, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 14739 + }, + { + "gameId": 83, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 34, + "duration": 18491 + }, + { + "gameId": 84, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 16264 + }, + { + "gameId": 85, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 5.5, + "scoreDiff": 11.5, + "moveCount": 33, + "duration": 18033 + }, + { + "gameId": 86, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 19504 + }, + { + "gameId": 87, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 18156 + }, + { + "gameId": 88, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 25, + "duration": 13086 + }, + { + "gameId": 89, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 17.5, + "scoreDiff": -12.5, + "moveCount": 34, + "duration": 19008 + }, + { + "gameId": 90, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 50, + "duration": 29892 + }, + { + "gameId": 91, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 33, + "duration": 17943 + }, + { + "gameId": 92, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 2.5, + "model2Score": 3, + "scoreDiff": 0.5, + "moveCount": 7, + "duration": 3629 + }, + { + "gameId": 93, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 15442 + }, + { + "gameId": 94, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 4.5, + "model2Score": 3, + "scoreDiff": -1.5, + "moveCount": 8, + "duration": 4161 + }, + { + "gameId": 95, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 29, + "duration": 14996 + }, + { + "gameId": 96, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 2.5, + "model2Score": 1, + "scoreDiff": -1.5, + "moveCount": 6, + "duration": 3085 + }, + { + "gameId": 97, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 29, + "duration": 15145 + }, + { + "gameId": 98, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 10, + "scoreDiff": 2.5, + "moveCount": 19, + "duration": 10218 + }, + { + "gameId": 99, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 18435 + }, + { + "gameId": 100, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 15, + "scoreDiff": 6.5, + "moveCount": 32, + "duration": 17277 + } + ] + }, + "timestamp": "2025-12-25T00:57:19.002Z" +} \ No newline at end of file diff --git a/trigo-web/tools/output/battle_results/battle_2025-12-28T01-30-27-705Z.json b/trigo-web/tools/output/battle_results/battle_2025-12-28T01-30-27-705Z.json new file mode 100644 index 0000000000000000000000000000000000000000..ce22b5a44ab020d806eef8972d0475ddd9c83a42 --- /dev/null +++ b/trigo-web/tools/output/battle_results/battle_2025-12-28T01-30-27-705Z.json @@ -0,0 +1,10035 @@ +{ + "config": { + "model1Tree": "/home/camus/work/trigoRL/outputs/trigor/20251226-trigo-value-llama-l6-h64-it2_251221-value0.01/LlamaCausalLM_ep0030_tree.onnx", + "model2Tree": "/home/camus/work/trigoRL/outputs/trigor/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_tree.onnx", + "model2Eval": "/home/camus/work/trigoRL/outputs/trigor/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_evaluation.onnx", + "boardShape": { + "x": 5, + "y": 5, + "z": 1 + }, + "numGames": 1000, + "temperature": 1, + "useMCTS": false, + "mctsSimulations": 600, + "komi": 0.5 + }, + "statistics": { + "totalGames": 1000, + "model1Wins": 670, + "model2Wins": 330, + "draws": 0, + "model1WinRate": 67, + "model2WinRate": 33, + "model1AsBlackWins": 312, + "model1AsWhiteWins": 358, + "model2AsBlackWins": 142, + "model2AsWhiteWins": 188, + "averageGameLength": 28.601, + "averageScoreDiff": -0.966, + "totalTime": 180802, + "results": [ + { + "gameId": 1, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 7.5, + "scoreDiff": 10.5, + "moveCount": 35, + "duration": 194 + }, + { + "gameId": 2, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 7, + "scoreDiff": -8.5, + "moveCount": 30, + "duration": 127 + }, + { + "gameId": 3, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 28, + "duration": 154 + }, + { + "gameId": 4, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 81 + }, + { + "gameId": 5, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 8.5, + "scoreDiff": 7.5, + "moveCount": 27, + "duration": 85 + }, + { + "gameId": 6, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 15, + "scoreDiff": 7.5, + "moveCount": 25, + "duration": 96 + }, + { + "gameId": 7, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 29, + "duration": 86 + }, + { + "gameId": 8, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 7, + "scoreDiff": 0.5, + "moveCount": 15, + "duration": 39 + }, + { + "gameId": 9, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 10, + "model2Score": 9.5, + "scoreDiff": 0.5, + "moveCount": 21, + "duration": 114 + }, + { + "gameId": 10, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 102 + }, + { + "gameId": 11, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 15.5, + "scoreDiff": -6.5, + "moveCount": 28, + "duration": 81 + }, + { + "gameId": 12, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 81 + }, + { + "gameId": 13, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 8.5, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 90 + }, + { + "gameId": 14, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 12, + "scoreDiff": 3.5, + "moveCount": 25, + "duration": 79 + }, + { + "gameId": 15, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 25, + "duration": 76 + }, + { + "gameId": 16, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 75 + }, + { + "gameId": 17, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 4.5, + "scoreDiff": 13.5, + "moveCount": 41, + "duration": 156 + }, + { + "gameId": 18, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 84 + }, + { + "gameId": 19, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 9, + "model2Score": 8.5, + "scoreDiff": 0.5, + "moveCount": 19, + "duration": 55 + }, + { + "gameId": 20, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 28, + "duration": 87 + }, + { + "gameId": 21, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 39, + "duration": 160 + }, + { + "gameId": 22, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 8, + "scoreDiff": -1.5, + "moveCount": 20, + "duration": 54 + }, + { + "gameId": 23, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 29, + "duration": 108 + }, + { + "gameId": 24, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 89 + }, + { + "gameId": 25, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 25, + "duration": 75 + }, + { + "gameId": 26, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 28, + "duration": 85 + }, + { + "gameId": 27, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 1.5, + "scoreDiff": 16.5, + "moveCount": 35, + "duration": 134 + }, + { + "gameId": 28, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 27, + "duration": 87 + }, + { + "gameId": 29, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 25, + "duration": 85 + }, + { + "gameId": 30, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 17, + "scoreDiff": 10.5, + "moveCount": 39, + "duration": 147 + }, + { + "gameId": 31, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 14.5, + "scoreDiff": -8.5, + "moveCount": 28, + "duration": 102 + }, + { + "gameId": 32, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 7, + "scoreDiff": -8.5, + "moveCount": 32, + "duration": 105 + }, + { + "gameId": 33, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 9.5, + "scoreDiff": 6.5, + "moveCount": 27, + "duration": 81 + }, + { + "gameId": 34, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 108 + }, + { + "gameId": 35, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 86 + }, + { + "gameId": 36, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 15, + "scoreDiff": 5.5, + "moveCount": 26, + "duration": 95 + }, + { + "gameId": 37, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 29, + "duration": 86 + }, + { + "gameId": 38, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 28, + "duration": 88 + }, + { + "gameId": 39, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 25, + "duration": 72 + }, + { + "gameId": 40, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 34, + "duration": 108 + }, + { + "gameId": 41, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 23, + "model2Score": 1.5, + "scoreDiff": 21.5, + "moveCount": 31, + "duration": 89 + }, + { + "gameId": 42, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 12, + "scoreDiff": -0.5, + "moveCount": 33, + "duration": 106 + }, + { + "gameId": 43, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 26, + "duration": 71 + }, + { + "gameId": 44, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 3.5, + "model2Score": 3, + "scoreDiff": -0.5, + "moveCount": 8, + "duration": 18 + }, + { + "gameId": 45, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 8.5, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 83 + }, + { + "gameId": 46, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 8, + "scoreDiff": -3.5, + "moveCount": 22, + "duration": 68 + }, + { + "gameId": 47, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 15.5, + "scoreDiff": -9.5, + "moveCount": 31, + "duration": 121 + }, + { + "gameId": 48, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 7, + "scoreDiff": 0.5, + "moveCount": 15, + "duration": 49 + }, + { + "gameId": 49, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 30, + "duration": 93 + }, + { + "gameId": 50, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 27, + "duration": 85 + }, + { + "gameId": 51, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 29, + "duration": 91 + }, + { + "gameId": 52, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 15, + "scoreDiff": 4.5, + "moveCount": 33, + "duration": 108 + }, + { + "gameId": 53, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 27, + "duration": 81 + }, + { + "gameId": 54, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 9, + "scoreDiff": -6.5, + "moveCount": 32, + "duration": 120 + }, + { + "gameId": 55, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 29, + "duration": 99 + }, + { + "gameId": 56, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 6, + "scoreDiff": -1.5, + "moveCount": 16, + "duration": 48 + }, + { + "gameId": 57, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 25, + "duration": 89 + }, + { + "gameId": 58, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 16, + "scoreDiff": 6.5, + "moveCount": 27, + "duration": 104 + }, + { + "gameId": 59, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 31, + "duration": 100 + }, + { + "gameId": 60, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 7, + "scoreDiff": -10.5, + "moveCount": 34, + "duration": 121 + }, + { + "gameId": 61, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 72 + }, + { + "gameId": 62, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 21.5, + "model2Score": 2, + "scoreDiff": -19.5, + "moveCount": 36, + "duration": 124 + }, + { + "gameId": 63, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 43, + "duration": 175 + }, + { + "gameId": 64, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 8, + "scoreDiff": -3.5, + "moveCount": 24, + "duration": 101 + }, + { + "gameId": 65, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 11.5, + "scoreDiff": 2.5, + "moveCount": 20, + "duration": 73 + }, + { + "gameId": 66, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 15, + "scoreDiff": 9.5, + "moveCount": 33, + "duration": 125 + }, + { + "gameId": 67, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 80 + }, + { + "gameId": 68, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 31, + "duration": 116 + }, + { + "gameId": 69, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 9.5, + "scoreDiff": 2.5, + "moveCount": 23, + "duration": 72 + }, + { + "gameId": 70, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 24, + "duration": 73 + }, + { + "gameId": 71, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 13.5, + "scoreDiff": -1.5, + "moveCount": 31, + "duration": 101 + }, + { + "gameId": 72, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 13, + "scoreDiff": 3.5, + "moveCount": 29, + "duration": 104 + }, + { + "gameId": 73, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 74 + }, + { + "gameId": 74, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 21.5, + "model2Score": 3, + "scoreDiff": -18.5, + "moveCount": 38, + "duration": 119 + }, + { + "gameId": 75, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 29, + "duration": 102 + }, + { + "gameId": 76, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 4.5, + "model2Score": 4, + "scoreDiff": -0.5, + "moveCount": 10, + "duration": 30 + }, + { + "gameId": 77, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 37, + "duration": 126 + }, + { + "gameId": 78, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 2.5, + "model2Score": 12, + "scoreDiff": 9.5, + "moveCount": 31, + "duration": 99 + }, + { + "gameId": 79, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 6.5, + "scoreDiff": 11.5, + "moveCount": 31, + "duration": 98 + }, + { + "gameId": 80, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 136 + }, + { + "gameId": 81, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 22, + "duration": 59 + }, + { + "gameId": 82, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 8, + "scoreDiff": -1.5, + "moveCount": 21, + "duration": 60 + }, + { + "gameId": 83, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 13.5, + "scoreDiff": -1.5, + "moveCount": 31, + "duration": 99 + }, + { + "gameId": 84, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 38, + "duration": 134 + }, + { + "gameId": 85, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 33, + "duration": 155 + }, + { + "gameId": 86, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 17, + "scoreDiff": 10.5, + "moveCount": 31, + "duration": 109 + }, + { + "gameId": 87, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 33, + "duration": 99 + }, + { + "gameId": 88, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 7, + "scoreDiff": -6.5, + "moveCount": 28, + "duration": 92 + }, + { + "gameId": 89, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 82 + }, + { + "gameId": 90, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 1.5, + "model2Score": 1, + "scoreDiff": -0.5, + "moveCount": 4, + "duration": 11 + }, + { + "gameId": 91, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 71 + }, + { + "gameId": 92, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 95 + }, + { + "gameId": 93, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 9.5, + "scoreDiff": 2.5, + "moveCount": 24, + "duration": 66 + }, + { + "gameId": 94, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 23.5, + "model2Score": 1, + "scoreDiff": -22.5, + "moveCount": 44, + "duration": 158 + }, + { + "gameId": 95, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 10.5, + "scoreDiff": 4.5, + "moveCount": 31, + "duration": 88 + }, + { + "gameId": 96, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 8, + "scoreDiff": 0.5, + "moveCount": 16, + "duration": 46 + }, + { + "gameId": 97, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 31, + "duration": 91 + }, + { + "gameId": 98, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 8, + "scoreDiff": -8.5, + "moveCount": 34, + "duration": 121 + }, + { + "gameId": 99, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 15.5, + "scoreDiff": -7.5, + "moveCount": 30, + "duration": 83 + }, + { + "gameId": 100, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 14, + "scoreDiff": 3.5, + "moveCount": 29, + "duration": 86 + }, + { + "gameId": 101, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 13.5, + "scoreDiff": -5.5, + "moveCount": 29, + "duration": 90 + }, + { + "gameId": 102, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 3.5, + "model2Score": 2, + "scoreDiff": -1.5, + "moveCount": 8, + "duration": 18 + }, + { + "gameId": 103, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 12.5, + "scoreDiff": -3.5, + "moveCount": 29, + "duration": 84 + }, + { + "gameId": 104, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 11, + "scoreDiff": -2.5, + "moveCount": 23, + "duration": 70 + }, + { + "gameId": 105, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 35, + "duration": 113 + }, + { + "gameId": 106, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 103 + }, + { + "gameId": 107, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 13.5, + "scoreDiff": -1.5, + "moveCount": 30, + "duration": 96 + }, + { + "gameId": 108, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 77 + }, + { + "gameId": 109, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 21, + "model2Score": 3.5, + "scoreDiff": 17.5, + "moveCount": 43, + "duration": 164 + }, + { + "gameId": 110, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 99 + }, + { + "gameId": 111, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 15.5, + "scoreDiff": -6.5, + "moveCount": 34, + "duration": 113 + }, + { + "gameId": 112, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 12, + "scoreDiff": 4.5, + "moveCount": 23, + "duration": 64 + }, + { + "gameId": 113, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 9.5, + "scoreDiff": 6.5, + "moveCount": 26, + "duration": 83 + }, + { + "gameId": 114, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 24, + "duration": 72 + }, + { + "gameId": 115, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 28, + "duration": 85 + }, + { + "gameId": 116, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 70 + }, + { + "gameId": 117, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 89 + }, + { + "gameId": 118, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 12, + "scoreDiff": 3.5, + "moveCount": 21, + "duration": 58 + }, + { + "gameId": 119, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 12.5, + "scoreDiff": -3.5, + "moveCount": 25, + "duration": 80 + }, + { + "gameId": 120, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 8, + "scoreDiff": -8.5, + "moveCount": 26, + "duration": 79 + }, + { + "gameId": 121, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 30, + "duration": 101 + }, + { + "gameId": 122, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 115 + }, + { + "gameId": 123, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 26, + "duration": 101 + }, + { + "gameId": 124, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 7, + "scoreDiff": -11.5, + "moveCount": 42, + "duration": 194 + }, + { + "gameId": 125, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 23, + "duration": 82 + }, + { + "gameId": 126, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 8, + "scoreDiff": -0.5, + "moveCount": 19, + "duration": 75 + }, + { + "gameId": 127, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 24, + "duration": 98 + }, + { + "gameId": 128, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 5.5, + "model2Score": 3, + "scoreDiff": -2.5, + "moveCount": 12, + "duration": 38 + }, + { + "gameId": 129, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 23, + "duration": 85 + }, + { + "gameId": 130, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 44, + "duration": 229 + }, + { + "gameId": 131, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 25, + "duration": 85 + }, + { + "gameId": 132, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 36, + "duration": 174 + }, + { + "gameId": 133, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 27, + "duration": 108 + }, + { + "gameId": 134, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 26, + "duration": 96 + }, + { + "gameId": 135, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 7.5, + "scoreDiff": 7.5, + "moveCount": 29, + "duration": 109 + }, + { + "gameId": 136, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 12, + "scoreDiff": 2.5, + "moveCount": 30, + "duration": 129 + }, + { + "gameId": 137, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 29, + "duration": 122 + }, + { + "gameId": 138, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 22, + "duration": 80 + }, + { + "gameId": 139, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 10.5, + "scoreDiff": 4.5, + "moveCount": 33, + "duration": 132 + }, + { + "gameId": 140, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 30, + "duration": 114 + }, + { + "gameId": 141, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 97 + }, + { + "gameId": 142, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 26, + "duration": 98 + }, + { + "gameId": 143, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 13.5, + "scoreDiff": -4.5, + "moveCount": 32, + "duration": 143 + }, + { + "gameId": 144, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 32, + "duration": 147 + }, + { + "gameId": 145, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 144 + }, + { + "gameId": 146, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 22.5, + "model2Score": 2, + "scoreDiff": -20.5, + "moveCount": 40, + "duration": 285 + }, + { + "gameId": 147, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 14.5, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 148 + }, + { + "gameId": 148, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 11, + "scoreDiff": 1.5, + "moveCount": 23, + "duration": 138 + }, + { + "gameId": 149, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 8.5, + "scoreDiff": 7.5, + "moveCount": 31, + "duration": 209 + }, + { + "gameId": 150, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 7, + "scoreDiff": -5.5, + "moveCount": 24, + "duration": 123 + }, + { + "gameId": 151, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 31, + "duration": 180 + }, + { + "gameId": 152, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 262 + }, + { + "gameId": 153, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 2.5, + "scoreDiff": 11.5, + "moveCount": 32, + "duration": 210 + }, + { + "gameId": 154, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 12, + "scoreDiff": -0.5, + "moveCount": 31, + "duration": 195 + }, + { + "gameId": 155, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 30, + "duration": 253 + }, + { + "gameId": 156, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 3.5, + "model2Score": 4, + "scoreDiff": 0.5, + "moveCount": 9, + "duration": 74 + }, + { + "gameId": 157, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 29, + "duration": 221 + }, + { + "gameId": 158, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 4, + "scoreDiff": -13.5, + "moveCount": 36, + "duration": 448 + }, + { + "gameId": 159, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 5.5, + "scoreDiff": 11.5, + "moveCount": 31, + "duration": 298 + }, + { + "gameId": 160, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 4, + "scoreDiff": -15.5, + "moveCount": 36, + "duration": 349 + }, + { + "gameId": 161, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 237 + }, + { + "gameId": 162, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 15, + "scoreDiff": 7.5, + "moveCount": 29, + "duration": 224 + }, + { + "gameId": 163, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 6.5, + "scoreDiff": 7.5, + "moveCount": 33, + "duration": 247 + }, + { + "gameId": 164, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 20.5, + "model2Score": 4, + "scoreDiff": -16.5, + "moveCount": 34, + "duration": 184 + }, + { + "gameId": 165, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 12.5, + "scoreDiff": -3.5, + "moveCount": 27, + "duration": 130 + }, + { + "gameId": 166, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 22, + "duration": 107 + }, + { + "gameId": 167, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 31, + "duration": 156 + }, + { + "gameId": 168, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 6, + "scoreDiff": 0.5, + "moveCount": 12, + "duration": 58 + }, + { + "gameId": 169, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 12.5, + "scoreDiff": -5.5, + "moveCount": 22, + "duration": 102 + }, + { + "gameId": 170, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 15, + "scoreDiff": 9.5, + "moveCount": 34, + "duration": 200 + }, + { + "gameId": 171, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 162 + }, + { + "gameId": 172, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 4, + "scoreDiff": -12.5, + "moveCount": 33, + "duration": 303 + }, + { + "gameId": 173, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 27, + "duration": 178 + }, + { + "gameId": 174, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 18, + "scoreDiff": 13.5, + "moveCount": 31, + "duration": 249 + }, + { + "gameId": 175, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 8.5, + "scoreDiff": 7.5, + "moveCount": 31, + "duration": 273 + }, + { + "gameId": 176, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 12, + "scoreDiff": 1.5, + "moveCount": 28, + "duration": 162 + }, + { + "gameId": 177, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 136 + }, + { + "gameId": 178, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 42, + "duration": 318 + }, + { + "gameId": 179, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 27, + "duration": 145 + }, + { + "gameId": 180, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 32, + "duration": 176 + }, + { + "gameId": 181, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 142 + }, + { + "gameId": 182, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 7, + "scoreDiff": 0.5, + "moveCount": 14, + "duration": 57 + }, + { + "gameId": 183, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 9.5, + "scoreDiff": 6.5, + "moveCount": 33, + "duration": 217 + }, + { + "gameId": 184, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 22, + "duration": 103 + }, + { + "gameId": 185, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 14.5, + "scoreDiff": -5.5, + "moveCount": 28, + "duration": 219 + }, + { + "gameId": 186, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 16, + "scoreDiff": 8.5, + "moveCount": 35, + "duration": 315 + }, + { + "gameId": 187, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 14.5, + "scoreDiff": -6.5, + "moveCount": 29, + "duration": 276 + }, + { + "gameId": 188, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 42, + "duration": 291 + }, + { + "gameId": 189, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 4.5, + "scoreDiff": 15.5, + "moveCount": 29, + "duration": 156 + }, + { + "gameId": 190, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 6, + "scoreDiff": -1.5, + "moveCount": 15, + "duration": 70 + }, + { + "gameId": 191, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 41, + "duration": 292 + }, + { + "gameId": 192, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 20, + "duration": 113 + }, + { + "gameId": 193, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 13.5, + "scoreDiff": -1.5, + "moveCount": 38, + "duration": 245 + }, + { + "gameId": 194, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 2.5, + "model2Score": 17, + "scoreDiff": 14.5, + "moveCount": 29, + "duration": 140 + }, + { + "gameId": 195, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 97 + }, + { + "gameId": 196, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 15, + "scoreDiff": 4.5, + "moveCount": 26, + "duration": 127 + }, + { + "gameId": 197, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 7.5, + "scoreDiff": 9.5, + "moveCount": 35, + "duration": 224 + }, + { + "gameId": 198, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 28, + "duration": 187 + }, + { + "gameId": 199, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 15.5, + "scoreDiff": -7.5, + "moveCount": 28, + "duration": 174 + }, + { + "gameId": 200, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 251 + }, + { + "gameId": 201, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 4.5, + "scoreDiff": 15.5, + "moveCount": 33, + "duration": 172 + }, + { + "gameId": 202, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 13, + "scoreDiff": 1.5, + "moveCount": 31, + "duration": 226 + }, + { + "gameId": 203, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 23, + "duration": 137 + }, + { + "gameId": 204, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 7, + "scoreDiff": -8.5, + "moveCount": 28, + "duration": 137 + }, + { + "gameId": 205, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 7.5, + "scoreDiff": 7.5, + "moveCount": 27, + "duration": 208 + }, + { + "gameId": 206, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 5, + "scoreDiff": -13.5, + "moveCount": 34, + "duration": 201 + }, + { + "gameId": 207, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 5.5, + "scoreDiff": 13.5, + "moveCount": 39, + "duration": 319 + }, + { + "gameId": 208, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 20, + "duration": 88 + }, + { + "gameId": 209, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 4.5, + "scoreDiff": 12.5, + "moveCount": 31, + "duration": 189 + }, + { + "gameId": 210, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 23.5, + "model2Score": 1, + "scoreDiff": -22.5, + "moveCount": 42, + "duration": 236 + }, + { + "gameId": 211, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 172 + }, + { + "gameId": 212, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 8, + "scoreDiff": -3.5, + "moveCount": 22, + "duration": 99 + }, + { + "gameId": 213, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 24, + "duration": 117 + }, + { + "gameId": 214, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 21.5, + "model2Score": 3, + "scoreDiff": -18.5, + "moveCount": 46, + "duration": 424 + }, + { + "gameId": 215, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 29, + "duration": 149 + }, + { + "gameId": 216, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 24, + "duration": 126 + }, + { + "gameId": 217, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 28, + "duration": 145 + }, + { + "gameId": 218, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 1.5, + "model2Score": 1, + "scoreDiff": -0.5, + "moveCount": 4, + "duration": 12 + }, + { + "gameId": 219, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 4.5, + "scoreDiff": 14.5, + "moveCount": 37, + "duration": 170 + }, + { + "gameId": 220, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 15, + "scoreDiff": 5.5, + "moveCount": 27, + "duration": 145 + }, + { + "gameId": 221, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 133 + }, + { + "gameId": 222, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 17, + "scoreDiff": 11.5, + "moveCount": 35, + "duration": 228 + }, + { + "gameId": 223, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 14.5, + "scoreDiff": -3.5, + "moveCount": 33, + "duration": 232 + }, + { + "gameId": 224, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 26, + "duration": 139 + }, + { + "gameId": 225, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 33, + "duration": 248 + }, + { + "gameId": 226, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 10, + "scoreDiff": -5.5, + "moveCount": 31, + "duration": 151 + }, + { + "gameId": 227, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 3.5, + "scoreDiff": 15.5, + "moveCount": 37, + "duration": 199 + }, + { + "gameId": 228, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 4, + "scoreDiff": -6.5, + "moveCount": 22, + "duration": 103 + }, + { + "gameId": 229, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 26, + "duration": 102 + }, + { + "gameId": 230, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 139 + }, + { + "gameId": 231, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 219 + }, + { + "gameId": 232, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 137 + }, + { + "gameId": 233, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 125 + }, + { + "gameId": 234, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 13, + "scoreDiff": 4.5, + "moveCount": 23, + "duration": 126 + }, + { + "gameId": 235, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 155 + }, + { + "gameId": 236, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 17, + "scoreDiff": 9.5, + "moveCount": 39, + "duration": 238 + }, + { + "gameId": 237, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 5.5, + "scoreDiff": 11.5, + "moveCount": 33, + "duration": 192 + }, + { + "gameId": 238, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 5, + "scoreDiff": 0.5, + "moveCount": 10, + "duration": 31 + }, + { + "gameId": 239, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 33, + "duration": 170 + }, + { + "gameId": 240, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 36, + "duration": 240 + }, + { + "gameId": 241, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 28, + "duration": 143 + }, + { + "gameId": 242, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 34, + "duration": 251 + }, + { + "gameId": 243, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 146 + }, + { + "gameId": 244, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 108 + }, + { + "gameId": 245, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 10.5, + "scoreDiff": 4.5, + "moveCount": 29, + "duration": 168 + }, + { + "gameId": 246, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 1.5, + "model2Score": 1, + "scoreDiff": -0.5, + "moveCount": 4, + "duration": 9 + }, + { + "gameId": 247, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 171 + }, + { + "gameId": 248, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 203 + }, + { + "gameId": 249, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 124 + }, + { + "gameId": 250, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 7, + "scoreDiff": -10.5, + "moveCount": 34, + "duration": 202 + }, + { + "gameId": 251, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 149 + }, + { + "gameId": 252, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 10, + "scoreDiff": -5.5, + "moveCount": 34, + "duration": 252 + }, + { + "gameId": 253, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 31, + "duration": 218 + }, + { + "gameId": 254, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 137 + }, + { + "gameId": 255, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 33, + "duration": 240 + }, + { + "gameId": 256, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 4, + "scoreDiff": -12.5, + "moveCount": 26, + "duration": 156 + }, + { + "gameId": 257, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 35, + "duration": 247 + }, + { + "gameId": 258, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 13, + "scoreDiff": 6.5, + "moveCount": 34, + "duration": 224 + }, + { + "gameId": 259, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 14.5, + "scoreDiff": -6.5, + "moveCount": 28, + "duration": 165 + }, + { + "gameId": 260, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 11, + "scoreDiff": 1.5, + "moveCount": 29, + "duration": 164 + }, + { + "gameId": 261, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 16.5, + "scoreDiff": -11.5, + "moveCount": 34, + "duration": 206 + }, + { + "gameId": 262, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 187 + }, + { + "gameId": 263, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 8.5, + "scoreDiff": 3.5, + "moveCount": 29, + "duration": 162 + }, + { + "gameId": 264, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 6, + "scoreDiff": -12.5, + "moveCount": 36, + "duration": 226 + }, + { + "gameId": 265, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 36, + "duration": 209 + }, + { + "gameId": 266, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 20, + "duration": 108 + }, + { + "gameId": 267, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 15.5, + "scoreDiff": -6.5, + "moveCount": 34, + "duration": 199 + }, + { + "gameId": 268, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 22.5, + "model2Score": 2, + "scoreDiff": -20.5, + "moveCount": 28, + "duration": 165 + }, + { + "gameId": 269, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 8.5, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 117 + }, + { + "gameId": 270, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 118 + }, + { + "gameId": 271, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 11.5, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 162 + }, + { + "gameId": 272, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 12, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 110 + }, + { + "gameId": 273, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 125 + }, + { + "gameId": 274, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 7, + "scoreDiff": -9.5, + "moveCount": 28, + "duration": 171 + }, + { + "gameId": 275, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 26, + "duration": 138 + }, + { + "gameId": 276, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 97 + }, + { + "gameId": 277, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 11.5, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 192 + }, + { + "gameId": 278, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 8, + "scoreDiff": -0.5, + "moveCount": 18, + "duration": 124 + }, + { + "gameId": 279, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 17.5, + "scoreDiff": -10.5, + "moveCount": 34, + "duration": 333 + }, + { + "gameId": 280, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 12, + "scoreDiff": 1.5, + "moveCount": 23, + "duration": 115 + }, + { + "gameId": 281, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 9.5, + "scoreDiff": 1.5, + "moveCount": 23, + "duration": 110 + }, + { + "gameId": 282, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 25, + "duration": 164 + }, + { + "gameId": 283, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 31, + "duration": 189 + }, + { + "gameId": 284, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 6, + "scoreDiff": -8.5, + "moveCount": 36, + "duration": 212 + }, + { + "gameId": 285, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 21, + "model2Score": 2.5, + "scoreDiff": 18.5, + "moveCount": 39, + "duration": 269 + }, + { + "gameId": 286, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 3.5, + "model2Score": 2, + "scoreDiff": -1.5, + "moveCount": 8, + "duration": 24 + }, + { + "gameId": 287, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 12.5, + "scoreDiff": 0.5, + "moveCount": 29, + "duration": 243 + }, + { + "gameId": 288, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 196 + }, + { + "gameId": 289, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 5.5, + "scoreDiff": 14.5, + "moveCount": 39, + "duration": 319 + }, + { + "gameId": 290, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 8, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 132 + }, + { + "gameId": 291, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 21, + "model2Score": 3.5, + "scoreDiff": 17.5, + "moveCount": 31, + "duration": 203 + }, + { + "gameId": 292, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 34, + "duration": 290 + }, + { + "gameId": 293, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 35, + "duration": 323 + }, + { + "gameId": 294, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 31, + "duration": 258 + }, + { + "gameId": 295, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 207 + }, + { + "gameId": 296, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 9, + "scoreDiff": -7.5, + "moveCount": 31, + "duration": 216 + }, + { + "gameId": 297, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 29, + "duration": 215 + }, + { + "gameId": 298, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 8, + "scoreDiff": -4.5, + "moveCount": 24, + "duration": 164 + }, + { + "gameId": 299, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 250 + }, + { + "gameId": 300, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 5, + "scoreDiff": -9.5, + "moveCount": 26, + "duration": 219 + }, + { + "gameId": 301, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 21, + "model2Score": 1.5, + "scoreDiff": 19.5, + "moveCount": 37, + "duration": 398 + }, + { + "gameId": 302, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 3.5, + "model2Score": 21, + "scoreDiff": 17.5, + "moveCount": 29, + "duration": 287 + }, + { + "gameId": 303, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 33, + "duration": 227 + }, + { + "gameId": 304, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 231 + }, + { + "gameId": 305, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 14.5, + "scoreDiff": -8.5, + "moveCount": 31, + "duration": 248 + }, + { + "gameId": 306, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 253 + }, + { + "gameId": 307, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 27, + "duration": 129 + }, + { + "gameId": 308, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 7, + "scoreDiff": -7.5, + "moveCount": 26, + "duration": 132 + }, + { + "gameId": 309, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 13.5, + "scoreDiff": -4.5, + "moveCount": 24, + "duration": 200 + }, + { + "gameId": 310, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 11, + "scoreDiff": -2.5, + "moveCount": 28, + "duration": 190 + }, + { + "gameId": 311, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 25, + "duration": 126 + }, + { + "gameId": 312, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 10, + "scoreDiff": 0.5, + "moveCount": 20, + "duration": 146 + }, + { + "gameId": 313, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 30, + "duration": 239 + }, + { + "gameId": 314, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 17, + "scoreDiff": 10.5, + "moveCount": 29, + "duration": 147 + }, + { + "gameId": 315, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 195 + }, + { + "gameId": 316, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 23.5, + "model2Score": 1, + "scoreDiff": -22.5, + "moveCount": 38, + "duration": 295 + }, + { + "gameId": 317, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 38, + "duration": 292 + }, + { + "gameId": 318, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 173 + }, + { + "gameId": 319, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 29, + "duration": 151 + }, + { + "gameId": 320, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 156 + }, + { + "gameId": 321, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 17.5, + "scoreDiff": -12.5, + "moveCount": 36, + "duration": 318 + }, + { + "gameId": 322, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 7, + "scoreDiff": -7.5, + "moveCount": 26, + "duration": 189 + }, + { + "gameId": 323, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 31, + "duration": 194 + }, + { + "gameId": 324, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 6, + "scoreDiff": -10.5, + "moveCount": 28, + "duration": 175 + }, + { + "gameId": 325, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 31, + "duration": 184 + }, + { + "gameId": 326, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 192 + }, + { + "gameId": 327, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 4.5, + "scoreDiff": 15.5, + "moveCount": 43, + "duration": 287 + }, + { + "gameId": 328, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 46, + "duration": 475 + }, + { + "gameId": 329, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 176 + }, + { + "gameId": 330, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 272 + }, + { + "gameId": 331, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 5.5, + "scoreDiff": 10.5, + "moveCount": 32, + "duration": 219 + }, + { + "gameId": 332, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 133 + }, + { + "gameId": 333, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 33, + "duration": 205 + }, + { + "gameId": 334, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 133 + }, + { + "gameId": 335, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 4.5, + "scoreDiff": 13.5, + "moveCount": 31, + "duration": 285 + }, + { + "gameId": 336, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 7, + "scoreDiff": -3.5, + "moveCount": 20, + "duration": 96 + }, + { + "gameId": 337, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 27, + "duration": 175 + }, + { + "gameId": 338, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 8, + "scoreDiff": -0.5, + "moveCount": 18, + "duration": 87 + }, + { + "gameId": 339, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 25, + "duration": 169 + }, + { + "gameId": 340, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 7, + "scoreDiff": 0.5, + "moveCount": 15, + "duration": 92 + }, + { + "gameId": 341, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 198 + }, + { + "gameId": 342, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 12, + "scoreDiff": 1.5, + "moveCount": 23, + "duration": 120 + }, + { + "gameId": 343, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 9.5, + "scoreDiff": 1.5, + "moveCount": 22, + "duration": 219 + }, + { + "gameId": 344, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 21, + "duration": 144 + }, + { + "gameId": 345, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 50, + "duration": 648 + }, + { + "gameId": 346, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 32, + "duration": 197 + }, + { + "gameId": 347, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 12.5, + "scoreDiff": 0.5, + "moveCount": 31, + "duration": 219 + }, + { + "gameId": 348, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 4, + "scoreDiff": -3.5, + "moveCount": 14, + "duration": 57 + }, + { + "gameId": 349, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 239 + }, + { + "gameId": 350, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 7, + "scoreDiff": -5.5, + "moveCount": 22, + "duration": 156 + }, + { + "gameId": 351, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 193 + }, + { + "gameId": 352, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 28, + "duration": 211 + }, + { + "gameId": 353, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 28, + "duration": 197 + }, + { + "gameId": 354, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 147 + }, + { + "gameId": 355, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 8.5, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 169 + }, + { + "gameId": 356, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 12, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 144 + }, + { + "gameId": 357, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 10.5, + "scoreDiff": -1.5, + "moveCount": 27, + "duration": 195 + }, + { + "gameId": 358, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 6, + "scoreDiff": -8.5, + "moveCount": 30, + "duration": 169 + }, + { + "gameId": 359, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 15.5, + "scoreDiff": -7.5, + "moveCount": 28, + "duration": 174 + }, + { + "gameId": 360, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 130 + }, + { + "gameId": 361, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 43, + "duration": 436 + }, + { + "gameId": 362, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 128 + }, + { + "gameId": 363, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 24, + "duration": 109 + }, + { + "gameId": 364, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 44, + "duration": 566 + }, + { + "gameId": 365, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 195 + }, + { + "gameId": 366, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 30, + "duration": 192 + }, + { + "gameId": 367, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 35, + "duration": 268 + }, + { + "gameId": 368, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 201 + }, + { + "gameId": 369, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 6.5, + "scoreDiff": 9.5, + "moveCount": 30, + "duration": 160 + }, + { + "gameId": 370, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 12, + "scoreDiff": -1.5, + "moveCount": 31, + "duration": 313 + }, + { + "gameId": 371, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 26, + "duration": 190 + }, + { + "gameId": 372, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 15, + "scoreDiff": 6.5, + "moveCount": 29, + "duration": 245 + }, + { + "gameId": 373, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 35, + "duration": 286 + }, + { + "gameId": 374, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 6, + "scoreDiff": -1.5, + "moveCount": 16, + "duration": 121 + }, + { + "gameId": 375, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 14.5, + "scoreDiff": -5.5, + "moveCount": 30, + "duration": 296 + }, + { + "gameId": 376, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 23, + "duration": 168 + }, + { + "gameId": 377, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 5.5, + "scoreDiff": 11.5, + "moveCount": 32, + "duration": 230 + }, + { + "gameId": 378, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 218 + }, + { + "gameId": 379, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 10, + "model2Score": 8.5, + "scoreDiff": 1.5, + "moveCount": 21, + "duration": 141 + }, + { + "gameId": 380, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 10, + "scoreDiff": -5.5, + "moveCount": 28, + "duration": 221 + }, + { + "gameId": 381, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 27, + "duration": 131 + }, + { + "gameId": 382, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 10, + "scoreDiff": 0.5, + "moveCount": 21, + "duration": 172 + }, + { + "gameId": 383, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 31, + "duration": 231 + }, + { + "gameId": 384, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 7, + "scoreDiff": -9.5, + "moveCount": 36, + "duration": 224 + }, + { + "gameId": 385, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 12.5, + "scoreDiff": 0.5, + "moveCount": 29, + "duration": 158 + }, + { + "gameId": 386, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 21.5, + "model2Score": 3, + "scoreDiff": -18.5, + "moveCount": 42, + "duration": 314 + }, + { + "gameId": 387, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 4.5, + "scoreDiff": 14.5, + "moveCount": 36, + "duration": 225 + }, + { + "gameId": 388, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 40, + "duration": 294 + }, + { + "gameId": 389, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 14.5, + "scoreDiff": -9.5, + "moveCount": 35, + "duration": 251 + }, + { + "gameId": 390, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 9, + "scoreDiff": -6.5, + "moveCount": 34, + "duration": 293 + }, + { + "gameId": 391, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 14.5, + "scoreDiff": -5.5, + "moveCount": 24, + "duration": 163 + }, + { + "gameId": 392, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 8, + "scoreDiff": -0.5, + "moveCount": 19, + "duration": 79 + }, + { + "gameId": 393, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 21, + "model2Score": 3.5, + "scoreDiff": 17.5, + "moveCount": 43, + "duration": 354 + }, + { + "gameId": 394, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 11, + "scoreDiff": 2.5, + "moveCount": 21, + "duration": 139 + }, + { + "gameId": 395, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 22, + "model2Score": 2.5, + "scoreDiff": 19.5, + "moveCount": 39, + "duration": 342 + }, + { + "gameId": 396, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 40, + "duration": 412 + }, + { + "gameId": 397, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 110 + }, + { + "gameId": 398, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 13, + "scoreDiff": 8.5, + "moveCount": 42, + "duration": 423 + }, + { + "gameId": 399, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 10.5, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 231 + }, + { + "gameId": 400, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 176 + }, + { + "gameId": 401, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 194 + }, + { + "gameId": 402, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 107 + }, + { + "gameId": 403, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 156 + }, + { + "gameId": 404, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 9, + "scoreDiff": -6.5, + "moveCount": 28, + "duration": 137 + }, + { + "gameId": 405, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 15.5, + "scoreDiff": -5.5, + "moveCount": 26, + "duration": 111 + }, + { + "gameId": 406, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 34, + "duration": 258 + }, + { + "gameId": 407, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 7, + "model2Score": 6.5, + "scoreDiff": 0.5, + "moveCount": 15, + "duration": 75 + }, + { + "gameId": 408, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 172 + }, + { + "gameId": 409, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 4.5, + "scoreDiff": 11.5, + "moveCount": 32, + "duration": 163 + }, + { + "gameId": 410, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 34, + "duration": 205 + }, + { + "gameId": 411, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 29, + "duration": 144 + }, + { + "gameId": 412, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 15, + "scoreDiff": 5.5, + "moveCount": 27, + "duration": 139 + }, + { + "gameId": 413, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 140 + }, + { + "gameId": 414, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 11, + "scoreDiff": 2.5, + "moveCount": 20, + "duration": 131 + }, + { + "gameId": 415, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 33, + "duration": 202 + }, + { + "gameId": 416, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 2.5, + "model2Score": 2, + "scoreDiff": -0.5, + "moveCount": 6, + "duration": 28 + }, + { + "gameId": 417, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 14.5, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 224 + }, + { + "gameId": 418, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 6, + "scoreDiff": -5.5, + "moveCount": 22, + "duration": 111 + }, + { + "gameId": 419, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 197 + }, + { + "gameId": 420, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 17, + "scoreDiff": 9.5, + "moveCount": 41, + "duration": 349 + }, + { + "gameId": 421, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 41, + "duration": 401 + }, + { + "gameId": 422, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 333 + }, + { + "gameId": 423, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 35, + "duration": 214 + }, + { + "gameId": 424, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 29, + "duration": 230 + }, + { + "gameId": 425, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 10.5, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 177 + }, + { + "gameId": 426, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 20, + "scoreDiff": 15.5, + "moveCount": 35, + "duration": 296 + }, + { + "gameId": 427, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 188 + }, + { + "gameId": 428, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 166 + }, + { + "gameId": 429, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 26, + "duration": 198 + }, + { + "gameId": 430, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 210 + }, + { + "gameId": 431, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 25, + "duration": 115 + }, + { + "gameId": 432, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 185 + }, + { + "gameId": 433, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 145 + }, + { + "gameId": 434, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 19, + "scoreDiff": 13.5, + "moveCount": 35, + "duration": 209 + }, + { + "gameId": 435, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 17.5, + "scoreDiff": -9.5, + "moveCount": 38, + "duration": 214 + }, + { + "gameId": 436, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 7, + "scoreDiff": -8.5, + "moveCount": 28, + "duration": 232 + }, + { + "gameId": 437, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 6, + "model2Score": 4.5, + "scoreDiff": 1.5, + "moveCount": 11, + "duration": 58 + }, + { + "gameId": 438, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 22.5, + "model2Score": 2, + "scoreDiff": -20.5, + "moveCount": 40, + "duration": 351 + }, + { + "gameId": 439, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 28, + "duration": 135 + }, + { + "gameId": 440, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 5, + "scoreDiff": -13.5, + "moveCount": 32, + "duration": 289 + }, + { + "gameId": 441, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 11.5, + "scoreDiff": -1.5, + "moveCount": 24, + "duration": 143 + }, + { + "gameId": 442, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 8, + "scoreDiff": -3.5, + "moveCount": 24, + "duration": 204 + }, + { + "gameId": 443, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 174 + }, + { + "gameId": 444, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 27, + "duration": 176 + }, + { + "gameId": 445, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 29, + "duration": 155 + }, + { + "gameId": 446, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 25, + "duration": 189 + }, + { + "gameId": 447, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 110 + }, + { + "gameId": 448, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 381 + }, + { + "gameId": 449, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 11.5, + "scoreDiff": -2.5, + "moveCount": 23, + "duration": 102 + }, + { + "gameId": 450, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 27, + "duration": 193 + }, + { + "gameId": 451, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 13.5, + "scoreDiff": -1.5, + "moveCount": 32, + "duration": 260 + }, + { + "gameId": 452, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 8, + "scoreDiff": -3.5, + "moveCount": 22, + "duration": 124 + }, + { + "gameId": 453, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 27, + "duration": 165 + }, + { + "gameId": 454, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 3.5, + "model2Score": 15, + "scoreDiff": 11.5, + "moveCount": 32, + "duration": 221 + }, + { + "gameId": 455, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 29, + "duration": 248 + }, + { + "gameId": 456, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 20.5, + "model2Score": 3, + "scoreDiff": -17.5, + "moveCount": 36, + "duration": 302 + }, + { + "gameId": 457, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 12.5, + "scoreDiff": -3.5, + "moveCount": 29, + "duration": 146 + }, + { + "gameId": 458, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 28, + "duration": 188 + }, + { + "gameId": 459, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 35, + "duration": 295 + }, + { + "gameId": 460, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 11, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 140 + }, + { + "gameId": 461, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 5.5, + "scoreDiff": 11.5, + "moveCount": 35, + "duration": 228 + }, + { + "gameId": 462, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 5, + "scoreDiff": -11.5, + "moveCount": 32, + "duration": 223 + }, + { + "gameId": 463, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 31, + "duration": 188 + }, + { + "gameId": 464, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 11, + "scoreDiff": -3.5, + "moveCount": 33, + "duration": 280 + }, + { + "gameId": 465, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 8.5, + "scoreDiff": 7.5, + "moveCount": 37, + "duration": 272 + }, + { + "gameId": 466, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 27, + "duration": 150 + }, + { + "gameId": 467, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 29, + "duration": 170 + }, + { + "gameId": 468, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 8, + "scoreDiff": -8.5, + "moveCount": 28, + "duration": 154 + }, + { + "gameId": 469, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 3.5, + "scoreDiff": 14.5, + "moveCount": 37, + "duration": 260 + }, + { + "gameId": 470, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 9, + "scoreDiff": -1.5, + "moveCount": 23, + "duration": 156 + }, + { + "gameId": 471, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 234 + }, + { + "gameId": 472, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 177 + }, + { + "gameId": 473, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 3.5, + "scoreDiff": 13.5, + "moveCount": 39, + "duration": 282 + }, + { + "gameId": 474, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 14, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 197 + }, + { + "gameId": 475, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 31, + "duration": 193 + }, + { + "gameId": 476, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 7, + "scoreDiff": -4.5, + "moveCount": 20, + "duration": 109 + }, + { + "gameId": 477, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 31, + "duration": 219 + }, + { + "gameId": 478, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 3, + "scoreDiff": -15.5, + "moveCount": 32, + "duration": 252 + }, + { + "gameId": 479, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 164 + }, + { + "gameId": 480, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 23.5, + "model2Score": 1, + "scoreDiff": -22.5, + "moveCount": 46, + "duration": 351 + }, + { + "gameId": 481, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 198 + }, + { + "gameId": 482, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 8, + "scoreDiff": -3.5, + "moveCount": 24, + "duration": 137 + }, + { + "gameId": 483, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 146 + }, + { + "gameId": 484, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 28, + "duration": 176 + }, + { + "gameId": 485, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 25, + "duration": 142 + }, + { + "gameId": 486, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 188 + }, + { + "gameId": 487, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 15.5, + "scoreDiff": -9.5, + "moveCount": 34, + "duration": 296 + }, + { + "gameId": 488, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 6, + "scoreDiff": -9.5, + "moveCount": 31, + "duration": 264 + }, + { + "gameId": 489, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 27, + "duration": 173 + }, + { + "gameId": 490, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 10, + "scoreDiff": 1.5, + "moveCount": 18, + "duration": 73 + }, + { + "gameId": 491, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 33, + "duration": 170 + }, + { + "gameId": 492, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 10, + "scoreDiff": -5.5, + "moveCount": 30, + "duration": 204 + }, + { + "gameId": 493, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 16.5, + "scoreDiff": -9.5, + "moveCount": 43, + "duration": 355 + }, + { + "gameId": 494, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 152 + }, + { + "gameId": 495, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 13.5, + "scoreDiff": -4.5, + "moveCount": 29, + "duration": 163 + }, + { + "gameId": 496, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 8, + "scoreDiff": -1.5, + "moveCount": 20, + "duration": 89 + }, + { + "gameId": 497, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 26, + "duration": 125 + }, + { + "gameId": 498, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 14, + "scoreDiff": 7.5, + "moveCount": 30, + "duration": 143 + }, + { + "gameId": 499, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 29, + "duration": 155 + }, + { + "gameId": 500, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 26, + "duration": 118 + }, + { + "gameId": 501, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 23, + "model2Score": 1.5, + "scoreDiff": 21.5, + "moveCount": 37, + "duration": 207 + }, + { + "gameId": 502, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 13, + "scoreDiff": 6.5, + "moveCount": 35, + "duration": 212 + }, + { + "gameId": 503, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 15.5, + "scoreDiff": -5.5, + "moveCount": 34, + "duration": 198 + }, + { + "gameId": 504, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 142 + }, + { + "gameId": 505, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 29, + "duration": 144 + }, + { + "gameId": 506, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 224 + }, + { + "gameId": 507, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 4.5, + "scoreDiff": 15.5, + "moveCount": 33, + "duration": 208 + }, + { + "gameId": 508, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 28, + "duration": 135 + }, + { + "gameId": 509, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 25, + "duration": 103 + }, + { + "gameId": 510, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 11, + "scoreDiff": 1.5, + "moveCount": 23, + "duration": 92 + }, + { + "gameId": 511, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 5.5, + "scoreDiff": 11.5, + "moveCount": 27, + "duration": 142 + }, + { + "gameId": 512, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 8, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 122 + }, + { + "gameId": 513, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 10, + "model2Score": 8.5, + "scoreDiff": 1.5, + "moveCount": 21, + "duration": 109 + }, + { + "gameId": 514, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 31, + "duration": 170 + }, + { + "gameId": 515, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 37, + "duration": 197 + }, + { + "gameId": 516, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 22, + "duration": 91 + }, + { + "gameId": 517, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 2, + "model2Score": 21.5, + "scoreDiff": -19.5, + "moveCount": 26, + "duration": 113 + }, + { + "gameId": 518, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 23.5, + "model2Score": 1, + "scoreDiff": -22.5, + "moveCount": 42, + "duration": 223 + }, + { + "gameId": 519, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 7.5, + "scoreDiff": 7.5, + "moveCount": 31, + "duration": 165 + }, + { + "gameId": 520, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 7, + "scoreDiff": -7.5, + "moveCount": 26, + "duration": 132 + }, + { + "gameId": 521, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 16.5, + "scoreDiff": -11.5, + "moveCount": 33, + "duration": 191 + }, + { + "gameId": 522, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 131 + }, + { + "gameId": 523, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 109 + }, + { + "gameId": 524, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 14, + "scoreDiff": 5.5, + "moveCount": 34, + "duration": 197 + }, + { + "gameId": 525, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 8.5, + "scoreDiff": 8.5, + "moveCount": 32, + "duration": 162 + }, + { + "gameId": 526, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 13, + "scoreDiff": 1.5, + "moveCount": 33, + "duration": 189 + }, + { + "gameId": 527, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 160 + }, + { + "gameId": 528, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 135 + }, + { + "gameId": 529, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 2.5, + "scoreDiff": 17.5, + "moveCount": 35, + "duration": 271 + }, + { + "gameId": 530, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 39, + "duration": 277 + }, + { + "gameId": 531, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 21, + "model2Score": 3.5, + "scoreDiff": 17.5, + "moveCount": 41, + "duration": 250 + }, + { + "gameId": 532, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 31, + "duration": 190 + }, + { + "gameId": 533, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 123 + }, + { + "gameId": 534, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 133 + }, + { + "gameId": 535, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 170 + }, + { + "gameId": 536, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 122 + }, + { + "gameId": 537, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 24, + "duration": 106 + }, + { + "gameId": 538, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 11, + "scoreDiff": 1.5, + "moveCount": 22, + "duration": 110 + }, + { + "gameId": 539, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 9.5, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 158 + }, + { + "gameId": 540, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 89 + }, + { + "gameId": 541, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 28, + "duration": 151 + }, + { + "gameId": 542, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 14, + "scoreDiff": 5.5, + "moveCount": 40, + "duration": 265 + }, + { + "gameId": 543, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 3.5, + "scoreDiff": 16.5, + "moveCount": 32, + "duration": 179 + }, + { + "gameId": 544, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 43, + "duration": 337 + }, + { + "gameId": 545, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 13.5, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 121 + }, + { + "gameId": 546, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 7, + "scoreDiff": -3.5, + "moveCount": 18, + "duration": 88 + }, + { + "gameId": 547, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 33, + "duration": 224 + }, + { + "gameId": 548, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 41, + "duration": 394 + }, + { + "gameId": 549, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 24, + "duration": 214 + }, + { + "gameId": 550, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 36, + "duration": 320 + }, + { + "gameId": 551, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 140 + }, + { + "gameId": 552, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 6.5, + "model2Score": 5, + "scoreDiff": -1.5, + "moveCount": 14, + "duration": 51 + }, + { + "gameId": 553, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 181 + }, + { + "gameId": 554, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 13, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 188 + }, + { + "gameId": 555, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 8.5, + "scoreDiff": 2.5, + "moveCount": 25, + "duration": 127 + }, + { + "gameId": 556, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 42, + "duration": 317 + }, + { + "gameId": 557, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 18.5, + "scoreDiff": -12.5, + "moveCount": 30, + "duration": 166 + }, + { + "gameId": 558, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 36, + "duration": 292 + }, + { + "gameId": 559, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 10.5, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 205 + }, + { + "gameId": 560, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 21.5, + "model2Score": 1, + "scoreDiff": -20.5, + "moveCount": 48, + "duration": 505 + }, + { + "gameId": 561, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 134 + }, + { + "gameId": 562, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 1, + "scoreDiff": -18.5, + "moveCount": 34, + "duration": 208 + }, + { + "gameId": 563, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 17.5, + "scoreDiff": -12.5, + "moveCount": 34, + "duration": 239 + }, + { + "gameId": 564, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 11, + "scoreDiff": 2.5, + "moveCount": 20, + "duration": 97 + }, + { + "gameId": 565, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 14.5, + "scoreDiff": -7.5, + "moveCount": 29, + "duration": 155 + }, + { + "gameId": 566, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 36, + "duration": 403 + }, + { + "gameId": 567, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 222 + }, + { + "gameId": 568, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 346 + }, + { + "gameId": 569, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 31, + "duration": 236 + }, + { + "gameId": 570, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 12, + "scoreDiff": -0.5, + "moveCount": 28, + "duration": 142 + }, + { + "gameId": 571, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 17.5, + "scoreDiff": -12.5, + "moveCount": 36, + "duration": 284 + }, + { + "gameId": 572, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 12, + "scoreDiff": -1.5, + "moveCount": 32, + "duration": 271 + }, + { + "gameId": 573, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 27, + "duration": 188 + }, + { + "gameId": 574, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 27, + "duration": 234 + }, + { + "gameId": 575, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 28, + "duration": 202 + }, + { + "gameId": 576, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 128 + }, + { + "gameId": 577, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 13.5, + "scoreDiff": -1.5, + "moveCount": 22, + "duration": 124 + }, + { + "gameId": 578, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 28, + "duration": 140 + }, + { + "gameId": 579, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 29, + "duration": 161 + }, + { + "gameId": 580, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 220 + }, + { + "gameId": 581, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 7.5, + "scoreDiff": 9.5, + "moveCount": 33, + "duration": 266 + }, + { + "gameId": 582, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 9, + "scoreDiff": -5.5, + "moveCount": 30, + "duration": 189 + }, + { + "gameId": 583, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 6.5, + "scoreDiff": 11.5, + "moveCount": 29, + "duration": 173 + }, + { + "gameId": 584, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 13, + "scoreDiff": 1.5, + "moveCount": 31, + "duration": 186 + }, + { + "gameId": 585, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 14.5, + "scoreDiff": -7.5, + "moveCount": 27, + "duration": 126 + }, + { + "gameId": 586, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 20.5, + "model2Score": 2, + "scoreDiff": -18.5, + "moveCount": 32, + "duration": 236 + }, + { + "gameId": 587, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 31, + "duration": 166 + }, + { + "gameId": 588, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 6, + "scoreDiff": -7.5, + "moveCount": 26, + "duration": 199 + }, + { + "gameId": 589, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 5.5, + "scoreDiff": 13.5, + "moveCount": 39, + "duration": 346 + }, + { + "gameId": 590, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 26, + "duration": 90 + }, + { + "gameId": 591, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 33, + "duration": 188 + }, + { + "gameId": 592, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 31, + "duration": 199 + }, + { + "gameId": 593, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 30, + "duration": 221 + }, + { + "gameId": 594, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 26, + "duration": 223 + }, + { + "gameId": 595, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 9.5, + "scoreDiff": 1.5, + "moveCount": 24, + "duration": 176 + }, + { + "gameId": 596, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 373 + }, + { + "gameId": 597, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 25, + "duration": 159 + }, + { + "gameId": 598, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 42, + "duration": 491 + }, + { + "gameId": 599, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 13.5, + "scoreDiff": -5.5, + "moveCount": 37, + "duration": 272 + }, + { + "gameId": 600, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 167 + }, + { + "gameId": 601, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 16.5, + "scoreDiff": -7.5, + "moveCount": 34, + "duration": 206 + }, + { + "gameId": 602, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 15, + "scoreDiff": 5.5, + "moveCount": 27, + "duration": 144 + }, + { + "gameId": 603, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 31, + "duration": 253 + }, + { + "gameId": 604, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 9, + "scoreDiff": -5.5, + "moveCount": 30, + "duration": 227 + }, + { + "gameId": 605, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 14.5, + "scoreDiff": -6.5, + "moveCount": 32, + "duration": 206 + }, + { + "gameId": 606, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 29, + "duration": 216 + }, + { + "gameId": 607, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 8.5, + "scoreDiff": 7.5, + "moveCount": 31, + "duration": 278 + }, + { + "gameId": 608, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 26, + "duration": 124 + }, + { + "gameId": 609, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 13.5, + "scoreDiff": -6.5, + "moveCount": 28, + "duration": 137 + }, + { + "gameId": 610, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 14, + "scoreDiff": 3.5, + "moveCount": 37, + "duration": 227 + }, + { + "gameId": 611, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 175 + }, + { + "gameId": 612, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 23, + "duration": 181 + }, + { + "gameId": 613, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 2, + "model2Score": 21.5, + "scoreDiff": -19.5, + "moveCount": 40, + "duration": 258 + }, + { + "gameId": 614, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 9, + "scoreDiff": -1.5, + "moveCount": 22, + "duration": 112 + }, + { + "gameId": 615, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 29, + "duration": 142 + }, + { + "gameId": 616, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 26, + "duration": 201 + }, + { + "gameId": 617, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 32, + "duration": 262 + }, + { + "gameId": 618, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 3.5, + "model2Score": 1, + "scoreDiff": -2.5, + "moveCount": 8, + "duration": 29 + }, + { + "gameId": 619, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 28, + "duration": 202 + }, + { + "gameId": 620, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 6, + "scoreDiff": -5.5, + "moveCount": 20, + "duration": 145 + }, + { + "gameId": 621, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 23, + "model2Score": 1.5, + "scoreDiff": 21.5, + "moveCount": 41, + "duration": 303 + }, + { + "gameId": 622, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 21.5, + "model2Score": 2, + "scoreDiff": -19.5, + "moveCount": 38, + "duration": 221 + }, + { + "gameId": 623, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 131 + }, + { + "gameId": 624, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 12.5, + "model2Score": 13, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 137 + }, + { + "gameId": 625, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 203 + }, + { + "gameId": 626, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 7, + "scoreDiff": -7.5, + "moveCount": 28, + "duration": 265 + }, + { + "gameId": 627, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 231 + }, + { + "gameId": 628, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 36, + "duration": 195 + }, + { + "gameId": 629, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 230 + }, + { + "gameId": 630, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 9, + "scoreDiff": -6.5, + "moveCount": 32, + "duration": 239 + }, + { + "gameId": 631, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 10, + "model2Score": 9.5, + "scoreDiff": 0.5, + "moveCount": 22, + "duration": 149 + }, + { + "gameId": 632, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 6, + "scoreDiff": -11.5, + "moveCount": 32, + "duration": 215 + }, + { + "gameId": 633, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 154 + }, + { + "gameId": 634, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 13, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 143 + }, + { + "gameId": 635, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 12.5, + "scoreDiff": -3.5, + "moveCount": 33, + "duration": 246 + }, + { + "gameId": 636, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 31, + "duration": 269 + }, + { + "gameId": 637, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 205 + }, + { + "gameId": 638, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 10, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 123 + }, + { + "gameId": 639, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 23, + "duration": 114 + }, + { + "gameId": 640, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 25, + "duration": 124 + }, + { + "gameId": 641, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 6.5, + "scoreDiff": 11.5, + "moveCount": 35, + "duration": 197 + }, + { + "gameId": 642, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 10, + "scoreDiff": 1.5, + "moveCount": 19, + "duration": 124 + }, + { + "gameId": 643, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 3.5, + "scoreDiff": 16.5, + "moveCount": 39, + "duration": 348 + }, + { + "gameId": 644, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 30, + "duration": 221 + }, + { + "gameId": 645, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 31, + "duration": 235 + }, + { + "gameId": 646, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 30, + "duration": 194 + }, + { + "gameId": 647, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 24, + "duration": 124 + }, + { + "gameId": 648, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 10, + "scoreDiff": 1.5, + "moveCount": 18, + "duration": 75 + }, + { + "gameId": 649, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 136 + }, + { + "gameId": 650, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 174 + }, + { + "gameId": 651, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 3.5, + "scoreDiff": 16.5, + "moveCount": 35, + "duration": 254 + }, + { + "gameId": 652, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 5, + "scoreDiff": -13.5, + "moveCount": 32, + "duration": 313 + }, + { + "gameId": 653, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 9.5, + "scoreDiff": 1.5, + "moveCount": 23, + "duration": 168 + }, + { + "gameId": 654, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 7, + "scoreDiff": -1.5, + "moveCount": 18, + "duration": 73 + }, + { + "gameId": 655, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 125 + }, + { + "gameId": 656, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 167 + }, + { + "gameId": 657, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 148 + }, + { + "gameId": 658, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 20.5, + "model2Score": 4, + "scoreDiff": -16.5, + "moveCount": 32, + "duration": 309 + }, + { + "gameId": 659, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 9, + "model2Score": 8.5, + "scoreDiff": 0.5, + "moveCount": 19, + "duration": 132 + }, + { + "gameId": 660, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 183 + }, + { + "gameId": 661, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 14.5, + "scoreDiff": -6.5, + "moveCount": 28, + "duration": 173 + }, + { + "gameId": 662, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 15, + "scoreDiff": 4.5, + "moveCount": 29, + "duration": 164 + }, + { + "gameId": 663, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 9, + "model2Score": 8.5, + "scoreDiff": 0.5, + "moveCount": 19, + "duration": 89 + }, + { + "gameId": 664, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 6, + "scoreDiff": 0.5, + "moveCount": 12, + "duration": 41 + }, + { + "gameId": 665, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 8.5, + "scoreDiff": 3.5, + "moveCount": 23, + "duration": 139 + }, + { + "gameId": 666, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 8, + "scoreDiff": -1.5, + "moveCount": 21, + "duration": 112 + }, + { + "gameId": 667, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 24, + "duration": 106 + }, + { + "gameId": 668, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 6, + "scoreDiff": -9.5, + "moveCount": 30, + "duration": 167 + }, + { + "gameId": 669, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 24, + "duration": 108 + }, + { + "gameId": 670, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 132 + }, + { + "gameId": 671, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 30, + "duration": 158 + }, + { + "gameId": 672, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 42, + "duration": 321 + }, + { + "gameId": 673, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 26, + "duration": 165 + }, + { + "gameId": 674, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 19, + "scoreDiff": 14.5, + "moveCount": 38, + "duration": 277 + }, + { + "gameId": 675, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 178 + }, + { + "gameId": 676, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 6, + "scoreDiff": -11.5, + "moveCount": 32, + "duration": 213 + }, + { + "gameId": 677, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 3, + "model2Score": 21.5, + "scoreDiff": -18.5, + "moveCount": 32, + "duration": 169 + }, + { + "gameId": 678, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 110 + }, + { + "gameId": 679, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 8.5, + "scoreDiff": 8.5, + "moveCount": 35, + "duration": 204 + }, + { + "gameId": 680, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 6, + "scoreDiff": -4.5, + "moveCount": 20, + "duration": 93 + }, + { + "gameId": 681, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 6.5, + "scoreDiff": 11.5, + "moveCount": 31, + "duration": 155 + }, + { + "gameId": 682, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 40, + "duration": 340 + }, + { + "gameId": 683, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 12.5, + "scoreDiff": -7.5, + "moveCount": 29, + "duration": 153 + }, + { + "gameId": 684, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 252 + }, + { + "gameId": 685, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 32, + "duration": 176 + }, + { + "gameId": 686, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 133 + }, + { + "gameId": 687, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 3.5, + "scoreDiff": 14.5, + "moveCount": 36, + "duration": 231 + }, + { + "gameId": 688, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 22, + "duration": 123 + }, + { + "gameId": 689, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 14.5, + "scoreDiff": -5.5, + "moveCount": 26, + "duration": 164 + }, + { + "gameId": 690, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 192 + }, + { + "gameId": 691, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 29, + "duration": 131 + }, + { + "gameId": 692, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 12, + "scoreDiff": -0.5, + "moveCount": 27, + "duration": 124 + }, + { + "gameId": 693, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 37, + "duration": 208 + }, + { + "gameId": 694, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 28, + "duration": 139 + }, + { + "gameId": 695, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 132 + }, + { + "gameId": 696, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 17, + "scoreDiff": 10.5, + "moveCount": 39, + "duration": 250 + }, + { + "gameId": 697, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 31, + "duration": 186 + }, + { + "gameId": 698, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 23.5, + "model2Score": 1, + "scoreDiff": -22.5, + "moveCount": 46, + "duration": 368 + }, + { + "gameId": 699, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 4, + "model2Score": 20.5, + "scoreDiff": -16.5, + "moveCount": 46, + "duration": 316 + }, + { + "gameId": 700, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 28, + "duration": 150 + }, + { + "gameId": 701, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 142 + }, + { + "gameId": 702, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 33, + "duration": 200 + }, + { + "gameId": 703, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 27, + "duration": 146 + }, + { + "gameId": 704, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 8, + "scoreDiff": -0.5, + "moveCount": 18, + "duration": 70 + }, + { + "gameId": 705, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 4.5, + "scoreDiff": 14.5, + "moveCount": 33, + "duration": 395 + }, + { + "gameId": 706, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 4, + "scoreDiff": -12.5, + "moveCount": 28, + "duration": 263 + }, + { + "gameId": 707, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 117 + }, + { + "gameId": 708, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 8, + "scoreDiff": -0.5, + "moveCount": 18, + "duration": 129 + }, + { + "gameId": 709, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 9.5, + "scoreDiff": 1.5, + "moveCount": 24, + "duration": 211 + }, + { + "gameId": 710, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 24, + "duration": 123 + }, + { + "gameId": 711, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 190 + }, + { + "gameId": 712, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 196 + }, + { + "gameId": 713, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 30, + "duration": 336 + }, + { + "gameId": 714, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 10, + "scoreDiff": 0.5, + "moveCount": 20, + "duration": 81 + }, + { + "gameId": 715, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 179 + }, + { + "gameId": 716, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 2.5, + "model2Score": 2, + "scoreDiff": -0.5, + "moveCount": 6, + "duration": 40 + }, + { + "gameId": 717, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 33, + "duration": 204 + }, + { + "gameId": 718, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 6, + "scoreDiff": -9.5, + "moveCount": 30, + "duration": 151 + }, + { + "gameId": 719, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 31, + "duration": 220 + }, + { + "gameId": 720, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 120 + }, + { + "gameId": 721, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 27, + "duration": 157 + }, + { + "gameId": 722, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 31, + "duration": 200 + }, + { + "gameId": 723, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 38, + "duration": 271 + }, + { + "gameId": 724, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 13, + "scoreDiff": 3.5, + "moveCount": 24, + "duration": 134 + }, + { + "gameId": 725, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 7.5, + "scoreDiff": 9.5, + "moveCount": 33, + "duration": 181 + }, + { + "gameId": 726, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 15, + "scoreDiff": 7.5, + "moveCount": 30, + "duration": 230 + }, + { + "gameId": 727, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 13.5, + "scoreDiff": -1.5, + "moveCount": 32, + "duration": 250 + }, + { + "gameId": 728, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 4, + "scoreDiff": -15.5, + "moveCount": 38, + "duration": 395 + }, + { + "gameId": 729, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 26, + "duration": 174 + }, + { + "gameId": 730, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 29, + "duration": 246 + }, + { + "gameId": 731, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 14.5, + "scoreDiff": -4.5, + "moveCount": 27, + "duration": 128 + }, + { + "gameId": 732, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 2.5, + "model2Score": 1, + "scoreDiff": -1.5, + "moveCount": 6, + "duration": 19 + }, + { + "gameId": 733, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 29, + "duration": 136 + }, + { + "gameId": 734, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 7, + "scoreDiff": -6.5, + "moveCount": 24, + "duration": 105 + }, + { + "gameId": 735, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 150 + }, + { + "gameId": 736, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 8, + "scoreDiff": -0.5, + "moveCount": 17, + "duration": 74 + }, + { + "gameId": 737, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 11.5, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 175 + }, + { + "gameId": 738, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 12, + "scoreDiff": 4.5, + "moveCount": 21, + "duration": 155 + }, + { + "gameId": 739, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 25, + "duration": 173 + }, + { + "gameId": 740, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 1.5, + "model2Score": 1, + "scoreDiff": -0.5, + "moveCount": 4, + "duration": 12 + }, + { + "gameId": 741, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 8.5, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 123 + }, + { + "gameId": 742, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 10, + "scoreDiff": 1.5, + "moveCount": 20, + "duration": 115 + }, + { + "gameId": 743, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 6.5, + "scoreDiff": 9.5, + "moveCount": 35, + "duration": 239 + }, + { + "gameId": 744, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 100 + }, + { + "gameId": 745, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 27, + "duration": 121 + }, + { + "gameId": 746, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 16, + "scoreDiff": 6.5, + "moveCount": 31, + "duration": 152 + }, + { + "gameId": 747, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 4.5, + "scoreDiff": 15.5, + "moveCount": 37, + "duration": 238 + }, + { + "gameId": 748, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 26, + "duration": 203 + }, + { + "gameId": 749, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 4.5, + "scoreDiff": 12.5, + "moveCount": 41, + "duration": 376 + }, + { + "gameId": 750, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 28, + "duration": 218 + }, + { + "gameId": 751, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 9.5, + "scoreDiff": 6.5, + "moveCount": 25, + "duration": 131 + }, + { + "gameId": 752, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 23.5, + "model2Score": 1, + "scoreDiff": -22.5, + "moveCount": 44, + "duration": 256 + }, + { + "gameId": 753, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 9.5, + "scoreDiff": -0.5, + "moveCount": 19, + "duration": 100 + }, + { + "gameId": 754, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 9, + "scoreDiff": -5.5, + "moveCount": 29, + "duration": 164 + }, + { + "gameId": 755, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 172 + }, + { + "gameId": 756, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 35, + "duration": 212 + }, + { + "gameId": 757, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 32, + "duration": 285 + }, + { + "gameId": 758, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 5, + "scoreDiff": -11.5, + "moveCount": 34, + "duration": 250 + }, + { + "gameId": 759, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 123 + }, + { + "gameId": 760, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 9, + "scoreDiff": -1.5, + "moveCount": 22, + "duration": 98 + }, + { + "gameId": 761, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 7.5, + "scoreDiff": 9.5, + "moveCount": 31, + "duration": 148 + }, + { + "gameId": 762, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 5, + "scoreDiff": -11.5, + "moveCount": 24, + "duration": 114 + }, + { + "gameId": 763, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 14.5, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 133 + }, + { + "gameId": 764, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 22, + "duration": 101 + }, + { + "gameId": 765, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 12.5, + "scoreDiff": -3.5, + "moveCount": 25, + "duration": 124 + }, + { + "gameId": 766, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 8, + "scoreDiff": -1.5, + "moveCount": 18, + "duration": 71 + }, + { + "gameId": 767, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 25, + "duration": 172 + }, + { + "gameId": 768, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 7, + "scoreDiff": -6.5, + "moveCount": 26, + "duration": 191 + }, + { + "gameId": 769, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 29, + "duration": 120 + }, + { + "gameId": 770, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 7, + "scoreDiff": -11.5, + "moveCount": 30, + "duration": 182 + }, + { + "gameId": 771, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 27, + "duration": 189 + }, + { + "gameId": 772, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 2, + "scoreDiff": -16.5, + "moveCount": 30, + "duration": 160 + }, + { + "gameId": 773, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 31, + "duration": 173 + }, + { + "gameId": 774, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 23.5, + "model2Score": 1, + "scoreDiff": -22.5, + "moveCount": 38, + "duration": 359 + }, + { + "gameId": 775, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 25, + "duration": 183 + }, + { + "gameId": 776, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 9, + "scoreDiff": 1.5, + "moveCount": 17, + "duration": 119 + }, + { + "gameId": 777, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 197 + }, + { + "gameId": 778, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 6, + "scoreDiff": -7.5, + "moveCount": 24, + "duration": 126 + }, + { + "gameId": 779, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 6.5, + "scoreDiff": 9.5, + "moveCount": 33, + "duration": 271 + }, + { + "gameId": 780, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 7, + "scoreDiff": -6.5, + "moveCount": 26, + "duration": 173 + }, + { + "gameId": 781, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 12.5, + "scoreDiff": 0.5, + "moveCount": 37, + "duration": 298 + }, + { + "gameId": 782, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 28, + "duration": 161 + }, + { + "gameId": 783, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 147 + }, + { + "gameId": 784, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 7, + "scoreDiff": -9.5, + "moveCount": 36, + "duration": 257 + }, + { + "gameId": 785, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 33, + "duration": 209 + }, + { + "gameId": 786, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 16, + "scoreDiff": 10.5, + "moveCount": 33, + "duration": 258 + }, + { + "gameId": 787, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 7.5, + "scoreDiff": 7.5, + "moveCount": 31, + "duration": 160 + }, + { + "gameId": 788, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 7, + "scoreDiff": -9.5, + "moveCount": 38, + "duration": 274 + }, + { + "gameId": 789, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 4, + "model2Score": 18.5, + "scoreDiff": -14.5, + "moveCount": 36, + "duration": 233 + }, + { + "gameId": 790, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 13, + "scoreDiff": 6.5, + "moveCount": 27, + "duration": 245 + }, + { + "gameId": 791, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 31, + "duration": 189 + }, + { + "gameId": 792, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 22.5, + "model2Score": 1, + "scoreDiff": -21.5, + "moveCount": 34, + "duration": 284 + }, + { + "gameId": 793, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 8.5, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 229 + }, + { + "gameId": 794, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 25, + "duration": 133 + }, + { + "gameId": 795, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 29, + "duration": 139 + }, + { + "gameId": 796, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 14, + "scoreDiff": 3.5, + "moveCount": 34, + "duration": 265 + }, + { + "gameId": 797, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 183 + }, + { + "gameId": 798, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 5.5, + "model2Score": 4, + "scoreDiff": -1.5, + "moveCount": 12, + "duration": 54 + }, + { + "gameId": 799, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 220 + }, + { + "gameId": 800, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 27, + "duration": 156 + }, + { + "gameId": 801, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 11.5, + "scoreDiff": 2.5, + "moveCount": 31, + "duration": 217 + }, + { + "gameId": 802, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 9, + "scoreDiff": -5.5, + "moveCount": 28, + "duration": 152 + }, + { + "gameId": 803, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 8.5, + "scoreDiff": 7.5, + "moveCount": 31, + "duration": 244 + }, + { + "gameId": 804, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 12, + "scoreDiff": 5.5, + "moveCount": 25, + "duration": 131 + }, + { + "gameId": 805, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 146 + }, + { + "gameId": 806, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 129 + }, + { + "gameId": 807, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 6.5, + "scoreDiff": 11.5, + "moveCount": 33, + "duration": 203 + }, + { + "gameId": 808, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 25, + "duration": 207 + }, + { + "gameId": 809, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 157 + }, + { + "gameId": 810, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 10, + "scoreDiff": -5.5, + "moveCount": 28, + "duration": 194 + }, + { + "gameId": 811, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 3.5, + "scoreDiff": 16.5, + "moveCount": 37, + "duration": 262 + }, + { + "gameId": 812, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 27, + "duration": 206 + }, + { + "gameId": 813, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 3.5, + "scoreDiff": 16.5, + "moveCount": 41, + "duration": 237 + }, + { + "gameId": 814, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 3.5, + "model2Score": 2, + "scoreDiff": -1.5, + "moveCount": 6, + "duration": 18 + }, + { + "gameId": 815, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 8.5, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 142 + }, + { + "gameId": 816, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 124 + }, + { + "gameId": 817, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 3.5, + "scoreDiff": 12.5, + "moveCount": 35, + "duration": 214 + }, + { + "gameId": 818, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 6, + "scoreDiff": -11.5, + "moveCount": 30, + "duration": 209 + }, + { + "gameId": 819, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 5.5, + "scoreDiff": 11.5, + "moveCount": 31, + "duration": 140 + }, + { + "gameId": 820, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 13, + "scoreDiff": 1.5, + "moveCount": 28, + "duration": 194 + }, + { + "gameId": 821, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 25, + "duration": 117 + }, + { + "gameId": 822, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 36, + "duration": 209 + }, + { + "gameId": 823, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 15.5, + "scoreDiff": -7.5, + "moveCount": 25, + "duration": 129 + }, + { + "gameId": 824, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 8, + "scoreDiff": -0.5, + "moveCount": 18, + "duration": 107 + }, + { + "gameId": 825, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 31, + "duration": 265 + }, + { + "gameId": 826, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 163 + }, + { + "gameId": 827, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 194 + }, + { + "gameId": 828, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 6, + "scoreDiff": -11.5, + "moveCount": 36, + "duration": 267 + }, + { + "gameId": 829, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 22, + "model2Score": 2.5, + "scoreDiff": 19.5, + "moveCount": 41, + "duration": 346 + }, + { + "gameId": 830, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 11, + "scoreDiff": -2.5, + "moveCount": 34, + "duration": 232 + }, + { + "gameId": 831, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 43, + "duration": 362 + }, + { + "gameId": 832, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 14, + "scoreDiff": 5.5, + "moveCount": 31, + "duration": 196 + }, + { + "gameId": 833, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 13.5, + "scoreDiff": -1.5, + "moveCount": 28, + "duration": 252 + }, + { + "gameId": 834, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 3, + "scoreDiff": -16.5, + "moveCount": 40, + "duration": 314 + }, + { + "gameId": 835, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 25, + "duration": 160 + }, + { + "gameId": 836, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 173 + }, + { + "gameId": 837, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 122 + }, + { + "gameId": 838, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 9, + "scoreDiff": -7.5, + "moveCount": 28, + "duration": 154 + }, + { + "gameId": 839, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 27, + "duration": 176 + }, + { + "gameId": 840, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 23.5, + "model2Score": 1, + "scoreDiff": -22.5, + "moveCount": 42, + "duration": 426 + }, + { + "gameId": 841, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 8.5, + "scoreDiff": 7.5, + "moveCount": 31, + "duration": 288 + }, + { + "gameId": 842, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 20.5, + "model2Score": 5, + "scoreDiff": -15.5, + "moveCount": 32, + "duration": 257 + }, + { + "gameId": 843, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 152 + }, + { + "gameId": 844, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 30, + "duration": 258 + }, + { + "gameId": 845, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 189 + }, + { + "gameId": 846, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 7, + "scoreDiff": -3.5, + "moveCount": 20, + "duration": 140 + }, + { + "gameId": 847, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 33, + "duration": 283 + }, + { + "gameId": 848, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 30, + "duration": 237 + }, + { + "gameId": 849, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 19.5, + "scoreDiff": -13.5, + "moveCount": 28, + "duration": 226 + }, + { + "gameId": 850, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 42, + "duration": 422 + }, + { + "gameId": 851, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 4, + "model2Score": 18.5, + "scoreDiff": -14.5, + "moveCount": 34, + "duration": 218 + }, + { + "gameId": 852, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 188 + }, + { + "gameId": 853, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 166 + }, + { + "gameId": 854, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 13, + "scoreDiff": 1.5, + "moveCount": 25, + "duration": 116 + }, + { + "gameId": 855, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 325 + }, + { + "gameId": 856, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 176 + }, + { + "gameId": 857, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 4.5, + "scoreDiff": 13.5, + "moveCount": 37, + "duration": 265 + }, + { + "gameId": 858, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 209 + }, + { + "gameId": 859, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 133 + }, + { + "gameId": 860, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 3.5, + "model2Score": 2, + "scoreDiff": -1.5, + "moveCount": 8, + "duration": 26 + }, + { + "gameId": 861, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 26, + "duration": 125 + }, + { + "gameId": 862, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 7, + "scoreDiff": -0.5, + "moveCount": 20, + "duration": 103 + }, + { + "gameId": 863, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 31, + "duration": 144 + }, + { + "gameId": 864, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 7, + "scoreDiff": -3.5, + "moveCount": 22, + "duration": 114 + }, + { + "gameId": 865, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 31, + "duration": 168 + }, + { + "gameId": 866, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 8, + "scoreDiff": -8.5, + "moveCount": 26, + "duration": 152 + }, + { + "gameId": 867, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 33, + "duration": 186 + }, + { + "gameId": 868, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 10, + "scoreDiff": 0.5, + "moveCount": 28, + "duration": 164 + }, + { + "gameId": 869, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 20.5, + "scoreDiff": -15.5, + "moveCount": 38, + "duration": 279 + }, + { + "gameId": 870, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 11, + "scoreDiff": -2.5, + "moveCount": 30, + "duration": 202 + }, + { + "gameId": 871, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 134 + }, + { + "gameId": 872, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 36, + "duration": 238 + }, + { + "gameId": 873, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 116 + }, + { + "gameId": 874, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 19, + "scoreDiff": 14.5, + "moveCount": 35, + "duration": 239 + }, + { + "gameId": 875, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 4, + "model2Score": 17.5, + "scoreDiff": -13.5, + "moveCount": 35, + "duration": 303 + }, + { + "gameId": 876, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 8, + "scoreDiff": -8.5, + "moveCount": 32, + "duration": 245 + }, + { + "gameId": 877, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 218 + }, + { + "gameId": 878, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 6, + "scoreDiff": -8.5, + "moveCount": 26, + "duration": 211 + }, + { + "gameId": 879, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 33, + "duration": 180 + }, + { + "gameId": 880, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 120 + }, + { + "gameId": 881, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 5.5, + "scoreDiff": 10.5, + "moveCount": 31, + "duration": 220 + }, + { + "gameId": 882, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 35, + "duration": 199 + }, + { + "gameId": 883, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 18.5, + "scoreDiff": -13.5, + "moveCount": 38, + "duration": 284 + }, + { + "gameId": 884, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 1.5, + "model2Score": 1, + "scoreDiff": -0.5, + "moveCount": 4, + "duration": 27 + }, + { + "gameId": 885, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 6.5, + "scoreDiff": 12.5, + "moveCount": 35, + "duration": 258 + }, + { + "gameId": 886, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 14, + "scoreDiff": 3.5, + "moveCount": 29, + "duration": 243 + }, + { + "gameId": 887, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 30, + "duration": 165 + }, + { + "gameId": 888, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 6, + "scoreDiff": -1.5, + "moveCount": 16, + "duration": 79 + }, + { + "gameId": 889, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 1, + "model2Score": 16.5, + "scoreDiff": -15.5, + "moveCount": 30, + "duration": 157 + }, + { + "gameId": 890, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 18, + "duration": 68 + }, + { + "gameId": 891, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 35, + "duration": 178 + }, + { + "gameId": 892, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 13, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 165 + }, + { + "gameId": 893, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 283 + }, + { + "gameId": 894, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 181 + }, + { + "gameId": 895, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 27, + "duration": 163 + }, + { + "gameId": 896, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 7, + "scoreDiff": -10.5, + "moveCount": 38, + "duration": 204 + }, + { + "gameId": 897, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 18.5, + "scoreDiff": -13.5, + "moveCount": 32, + "duration": 176 + }, + { + "gameId": 898, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 7, + "scoreDiff": -1.5, + "moveCount": 18, + "duration": 153 + }, + { + "gameId": 899, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 18.5, + "scoreDiff": -13.5, + "moveCount": 36, + "duration": 342 + }, + { + "gameId": 900, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 33, + "duration": 246 + }, + { + "gameId": 901, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 31, + "duration": 216 + }, + { + "gameId": 902, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 40, + "duration": 262 + }, + { + "gameId": 903, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 27, + "duration": 161 + }, + { + "gameId": 904, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 6, + "scoreDiff": -9.5, + "moveCount": 28, + "duration": 206 + }, + { + "gameId": 905, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 23, + "duration": 142 + }, + { + "gameId": 906, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 4, + "scoreDiff": -15.5, + "moveCount": 40, + "duration": 299 + }, + { + "gameId": 907, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 6.5, + "scoreDiff": 11.5, + "moveCount": 35, + "duration": 220 + }, + { + "gameId": 908, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 7, + "scoreDiff": -5.5, + "moveCount": 24, + "duration": 213 + }, + { + "gameId": 909, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 10.5, + "scoreDiff": 4.5, + "moveCount": 29, + "duration": 265 + }, + { + "gameId": 910, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 3, + "scoreDiff": -15.5, + "moveCount": 30, + "duration": 268 + }, + { + "gameId": 911, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 10, + "model2Score": 7.5, + "scoreDiff": 2.5, + "moveCount": 22, + "duration": 119 + }, + { + "gameId": 912, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 5.5, + "model2Score": 5, + "scoreDiff": -0.5, + "moveCount": 12, + "duration": 46 + }, + { + "gameId": 913, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 6.5, + "scoreDiff": 11.5, + "moveCount": 29, + "duration": 291 + }, + { + "gameId": 914, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 8, + "scoreDiff": -8.5, + "moveCount": 32, + "duration": 181 + }, + { + "gameId": 915, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 97 + }, + { + "gameId": 916, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 177 + }, + { + "gameId": 917, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 27, + "duration": 232 + }, + { + "gameId": 918, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 12, + "scoreDiff": -0.5, + "moveCount": 34, + "duration": 296 + }, + { + "gameId": 919, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 149 + }, + { + "gameId": 920, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 11, + "scoreDiff": -2.5, + "moveCount": 32, + "duration": 289 + }, + { + "gameId": 921, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 3.5, + "scoreDiff": 16.5, + "moveCount": 39, + "duration": 335 + }, + { + "gameId": 922, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 191 + }, + { + "gameId": 923, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 14.5, + "scoreDiff": -5.5, + "moveCount": 30, + "duration": 242 + }, + { + "gameId": 924, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 23, + "duration": 104 + }, + { + "gameId": 925, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 37, + "duration": 230 + }, + { + "gameId": 926, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 31, + "duration": 224 + }, + { + "gameId": 927, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 164 + }, + { + "gameId": 928, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 21, + "duration": 171 + }, + { + "gameId": 929, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 187 + }, + { + "gameId": 930, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 11, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 228 + }, + { + "gameId": 931, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 13.5, + "scoreDiff": -6.5, + "moveCount": 25, + "duration": 152 + }, + { + "gameId": 932, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 44, + "duration": 379 + }, + { + "gameId": 933, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 108 + }, + { + "gameId": 934, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 12, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 172 + }, + { + "gameId": 935, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 13.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 136 + }, + { + "gameId": 936, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 17, + "duration": 65 + }, + { + "gameId": 937, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 18.5, + "scoreDiff": -12.5, + "moveCount": 34, + "duration": 177 + }, + { + "gameId": 938, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 4, + "scoreDiff": -4.5, + "moveCount": 18, + "duration": 96 + }, + { + "gameId": 939, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 13.5, + "scoreDiff": -1.5, + "moveCount": 39, + "duration": 387 + }, + { + "gameId": 940, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 6, + "scoreDiff": -1.5, + "moveCount": 16, + "duration": 68 + }, + { + "gameId": 941, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 5.5, + "scoreDiff": 9.5, + "moveCount": 36, + "duration": 252 + }, + { + "gameId": 942, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 6, + "scoreDiff": -5.5, + "moveCount": 20, + "duration": 190 + }, + { + "gameId": 943, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 325 + }, + { + "gameId": 944, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 9, + "scoreDiff": -5.5, + "moveCount": 30, + "duration": 208 + }, + { + "gameId": 945, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 31, + "duration": 206 + }, + { + "gameId": 946, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 143 + }, + { + "gameId": 947, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 158 + }, + { + "gameId": 948, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 22, + "duration": 91 + }, + { + "gameId": 949, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 12.5, + "scoreDiff": -5.5, + "moveCount": 22, + "duration": 99 + }, + { + "gameId": 950, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 7, + "scoreDiff": -3.5, + "moveCount": 20, + "duration": 131 + }, + { + "gameId": 951, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 26, + "duration": 147 + }, + { + "gameId": 952, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 3.5, + "model2Score": 3, + "scoreDiff": -0.5, + "moveCount": 8, + "duration": 29 + }, + { + "gameId": 953, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 11.5, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 128 + }, + { + "gameId": 954, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 13, + "scoreDiff": 1.5, + "moveCount": 28, + "duration": 175 + }, + { + "gameId": 955, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 25, + "duration": 146 + }, + { + "gameId": 956, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 17, + "scoreDiff": 11.5, + "moveCount": 33, + "duration": 197 + }, + { + "gameId": 957, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 210 + }, + { + "gameId": 958, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 22, + "duration": 136 + }, + { + "gameId": 959, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 27, + "duration": 167 + }, + { + "gameId": 960, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 24, + "duration": 134 + }, + { + "gameId": 961, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 5.5, + "scoreDiff": 8.5, + "moveCount": 29, + "duration": 176 + }, + { + "gameId": 962, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 13, + "scoreDiff": 1.5, + "moveCount": 29, + "duration": 233 + }, + { + "gameId": 963, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 192 + }, + { + "gameId": 964, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 8, + "scoreDiff": -9.5, + "moveCount": 32, + "duration": 280 + }, + { + "gameId": 965, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 169 + }, + { + "gameId": 966, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 6, + "scoreDiff": -3.5, + "moveCount": 18, + "duration": 123 + }, + { + "gameId": 967, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 30, + "duration": 216 + }, + { + "gameId": 968, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 12, + "scoreDiff": -0.5, + "moveCount": 29, + "duration": 159 + }, + { + "gameId": 969, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 4, + "model2Score": 18.5, + "scoreDiff": -14.5, + "moveCount": 38, + "duration": 238 + }, + { + "gameId": 970, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 9, + "scoreDiff": 0.5, + "moveCount": 19, + "duration": 89 + }, + { + "gameId": 971, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 5.5, + "scoreDiff": 14.5, + "moveCount": 41, + "duration": 368 + }, + { + "gameId": 972, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 7, + "scoreDiff": -9.5, + "moveCount": 32, + "duration": 198 + }, + { + "gameId": 973, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 29, + "duration": 163 + }, + { + "gameId": 974, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 156 + }, + { + "gameId": 975, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 23, + "model2Score": 1.5, + "scoreDiff": 21.5, + "moveCount": 41, + "duration": 213 + }, + { + "gameId": 976, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 144 + }, + { + "gameId": 977, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 183 + }, + { + "gameId": 978, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 44, + "duration": 438 + }, + { + "gameId": 979, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 24, + "duration": 116 + }, + { + "gameId": 980, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 16, + "scoreDiff": 11.5, + "moveCount": 35, + "duration": 351 + }, + { + "gameId": 981, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 9, + "model2Score": 8.5, + "scoreDiff": 0.5, + "moveCount": 19, + "duration": 80 + }, + { + "gameId": 982, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 6, + "scoreDiff": -1.5, + "moveCount": 15, + "duration": 61 + }, + { + "gameId": 983, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 11.5, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 197 + }, + { + "gameId": 984, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 32, + "duration": 229 + }, + { + "gameId": 985, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 31, + "duration": 249 + }, + { + "gameId": 986, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 28, + "duration": 177 + }, + { + "gameId": 987, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 33, + "duration": 256 + }, + { + "gameId": 988, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 10, + "scoreDiff": 0.5, + "moveCount": 21, + "duration": 102 + }, + { + "gameId": 989, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 152 + }, + { + "gameId": 990, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 9, + "scoreDiff": 2.5, + "moveCount": 15, + "duration": 86 + }, + { + "gameId": 991, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 13.5, + "scoreDiff": -7.5, + "moveCount": 23, + "duration": 171 + }, + { + "gameId": 992, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 287 + }, + { + "gameId": 993, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 23, + "duration": 160 + }, + { + "gameId": 994, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 2, + "scoreDiff": -5.5, + "moveCount": 12, + "duration": 121 + }, + { + "gameId": 995, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 14.5, + "scoreDiff": -6.5, + "moveCount": 34, + "duration": 206 + }, + { + "gameId": 996, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 14, + "scoreDiff": 5.5, + "moveCount": 31, + "duration": 174 + }, + { + "gameId": 997, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 35, + "duration": 281 + }, + { + "gameId": 998, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 22, + "duration": 129 + }, + { + "gameId": 999, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 35, + "duration": 276 + }, + { + "gameId": 1000, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 181 + } + ] + }, + "timestamp": "2025-12-28T01:33:28.511Z" +} \ No newline at end of file diff --git a/trigo-web/tools/output/battle_results/battle_2025-12-28T01-30-42-052Z.json b/trigo-web/tools/output/battle_results/battle_2025-12-28T01-30-42-052Z.json new file mode 100644 index 0000000000000000000000000000000000000000..64d83e323cee9ecc0ec81811eaccb2d1a0bb3738 --- /dev/null +++ b/trigo-web/tools/output/battle_results/battle_2025-12-28T01-30-42-052Z.json @@ -0,0 +1,10035 @@ +{ + "config": { + "model1Tree": "/home/camus/work/trigoRL/outputs/trigor/20251226-trigo-value-llama-l12-h64-it2_251221-value0.01/LlamaCausalLM_ep0035_tree.onnx", + "model2Tree": "/home/camus/work/trigoRL/outputs/trigor/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_tree.onnx", + "model2Eval": "/home/camus/work/trigoRL/outputs/trigor/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_evaluation.onnx", + "boardShape": { + "x": 5, + "y": 5, + "z": 1 + }, + "numGames": 1000, + "temperature": 1, + "useMCTS": false, + "mctsSimulations": 600, + "komi": 0.5 + }, + "statistics": { + "totalGames": 1000, + "model1Wins": 641, + "model2Wins": 359, + "draws": 0, + "model1WinRate": 64.1, + "model2WinRate": 35.9, + "model1AsBlackWins": 284, + "model1AsWhiteWins": 357, + "model2AsBlackWins": 143, + "model2AsWhiteWins": 216, + "averageGameLength": 27.137, + "averageScoreDiff": -0.951, + "totalTime": 204701, + "results": [ + { + "gameId": 1, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 28, + "duration": 356 + }, + { + "gameId": 2, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 7, + "scoreDiff": -5.5, + "moveCount": 24, + "duration": 271 + }, + { + "gameId": 3, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 14.5, + "scoreDiff": -4.5, + "moveCount": 30, + "duration": 386 + }, + { + "gameId": 4, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 270 + }, + { + "gameId": 5, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 16.5, + "scoreDiff": -9.5, + "moveCount": 30, + "duration": 313 + }, + { + "gameId": 6, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 346 + }, + { + "gameId": 7, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 6.5, + "scoreDiff": 8.5, + "moveCount": 25, + "duration": 194 + }, + { + "gameId": 8, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 23, + "duration": 248 + }, + { + "gameId": 9, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 17.5, + "scoreDiff": -11.5, + "moveCount": 26, + "duration": 256 + }, + { + "gameId": 10, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 13, + "scoreDiff": 3.5, + "moveCount": 23, + "duration": 171 + }, + { + "gameId": 11, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 14.5, + "scoreDiff": -3.5, + "moveCount": 24, + "duration": 221 + }, + { + "gameId": 12, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 13, + "scoreDiff": 5.5, + "moveCount": 23, + "duration": 224 + }, + { + "gameId": 13, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 25, + "duration": 226 + }, + { + "gameId": 14, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 199 + }, + { + "gameId": 15, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 15.5, + "scoreDiff": -8.5, + "moveCount": 36, + "duration": 301 + }, + { + "gameId": 16, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 8, + "scoreDiff": -5.5, + "moveCount": 24, + "duration": 317 + }, + { + "gameId": 17, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 29, + "duration": 381 + }, + { + "gameId": 18, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 27, + "duration": 330 + }, + { + "gameId": 19, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 210 + }, + { + "gameId": 20, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 9, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 284 + }, + { + "gameId": 21, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 34, + "duration": 346 + }, + { + "gameId": 22, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 30, + "duration": 232 + }, + { + "gameId": 23, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 9.5, + "scoreDiff": 2.5, + "moveCount": 23, + "duration": 230 + }, + { + "gameId": 24, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 31, + "duration": 369 + }, + { + "gameId": 25, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 28, + "duration": 329 + }, + { + "gameId": 26, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 7, + "scoreDiff": -4.5, + "moveCount": 25, + "duration": 277 + }, + { + "gameId": 27, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 7.5, + "scoreDiff": 4.5, + "moveCount": 34, + "duration": 345 + }, + { + "gameId": 28, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 198 + }, + { + "gameId": 29, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 31, + "duration": 299 + }, + { + "gameId": 30, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 6, + "scoreDiff": -8.5, + "moveCount": 30, + "duration": 222 + }, + { + "gameId": 31, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 189 + }, + { + "gameId": 32, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 13, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 209 + }, + { + "gameId": 33, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 302 + }, + { + "gameId": 34, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 171 + }, + { + "gameId": 35, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 7.5, + "scoreDiff": 7.5, + "moveCount": 28, + "duration": 241 + }, + { + "gameId": 36, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 23, + "duration": 161 + }, + { + "gameId": 37, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 26, + "duration": 183 + }, + { + "gameId": 38, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 7, + "scoreDiff": -7.5, + "moveCount": 36, + "duration": 399 + }, + { + "gameId": 39, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 18.5, + "scoreDiff": -13.5, + "moveCount": 32, + "duration": 316 + }, + { + "gameId": 40, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 269 + }, + { + "gameId": 41, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 11.5, + "scoreDiff": -1.5, + "moveCount": 24, + "duration": 174 + }, + { + "gameId": 42, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 28, + "duration": 192 + }, + { + "gameId": 43, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 193 + }, + { + "gameId": 44, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 10, + "scoreDiff": 1.5, + "moveCount": 19, + "duration": 188 + }, + { + "gameId": 45, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 14.5, + "scoreDiff": -6.5, + "moveCount": 34, + "duration": 291 + }, + { + "gameId": 46, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 184 + }, + { + "gameId": 47, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 13.5, + "scoreDiff": -4.5, + "moveCount": 29, + "duration": 263 + }, + { + "gameId": 48, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 32, + "duration": 254 + }, + { + "gameId": 49, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 226 + }, + { + "gameId": 50, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 6, + "scoreDiff": -11.5, + "moveCount": 34, + "duration": 306 + }, + { + "gameId": 51, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 6.5, + "scoreDiff": 9.5, + "moveCount": 33, + "duration": 311 + }, + { + "gameId": 52, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 15, + "scoreDiff": 7.5, + "moveCount": 34, + "duration": 278 + }, + { + "gameId": 53, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 26, + "duration": 199 + }, + { + "gameId": 54, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 184 + }, + { + "gameId": 55, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 29, + "duration": 248 + }, + { + "gameId": 56, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 8, + "scoreDiff": -0.5, + "moveCount": 21, + "duration": 145 + }, + { + "gameId": 57, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 29, + "duration": 238 + }, + { + "gameId": 58, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 7, + "scoreDiff": -6.5, + "moveCount": 26, + "duration": 190 + }, + { + "gameId": 59, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 3.5, + "scoreDiff": 12.5, + "moveCount": 29, + "duration": 254 + }, + { + "gameId": 60, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 24, + "duration": 174 + }, + { + "gameId": 61, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 14.5, + "scoreDiff": -5.5, + "moveCount": 28, + "duration": 190 + }, + { + "gameId": 62, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 21, + "duration": 151 + }, + { + "gameId": 63, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 25, + "duration": 213 + }, + { + "gameId": 64, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 25, + "duration": 188 + }, + { + "gameId": 65, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 31, + "duration": 303 + }, + { + "gameId": 66, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 8, + "scoreDiff": -3.5, + "moveCount": 21, + "duration": 142 + }, + { + "gameId": 67, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 190 + }, + { + "gameId": 68, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 13, + "scoreDiff": 5.5, + "moveCount": 31, + "duration": 253 + }, + { + "gameId": 69, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 192 + }, + { + "gameId": 70, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 7, + "scoreDiff": 0.5, + "moveCount": 15, + "duration": 107 + }, + { + "gameId": 71, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 353 + }, + { + "gameId": 72, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 7, + "scoreDiff": -11.5, + "moveCount": 26, + "duration": 252 + }, + { + "gameId": 73, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 14.5, + "scoreDiff": -6.5, + "moveCount": 26, + "duration": 184 + }, + { + "gameId": 74, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 23, + "duration": 144 + }, + { + "gameId": 75, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 8.5, + "scoreDiff": 7.5, + "moveCount": 33, + "duration": 345 + }, + { + "gameId": 76, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 5.5, + "model2Score": 5, + "scoreDiff": -0.5, + "moveCount": 13, + "duration": 108 + }, + { + "gameId": 77, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 245 + }, + { + "gameId": 78, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 24, + "duration": 176 + }, + { + "gameId": 79, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 203 + }, + { + "gameId": 80, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 5.5, + "model2Score": 5, + "scoreDiff": -0.5, + "moveCount": 13, + "duration": 60 + }, + { + "gameId": 81, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 213 + }, + { + "gameId": 82, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 10, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 185 + }, + { + "gameId": 83, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 13.5, + "scoreDiff": -4.5, + "moveCount": 25, + "duration": 193 + }, + { + "gameId": 84, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 6, + "scoreDiff": -11.5, + "moveCount": 34, + "duration": 305 + }, + { + "gameId": 85, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 2, + "model2Score": 21.5, + "scoreDiff": -19.5, + "moveCount": 33, + "duration": 282 + }, + { + "gameId": 86, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 1.5, + "model2Score": 22, + "scoreDiff": 20.5, + "moveCount": 33, + "duration": 281 + }, + { + "gameId": 87, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 31, + "duration": 281 + }, + { + "gameId": 88, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 34, + "duration": 346 + }, + { + "gameId": 89, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 202 + }, + { + "gameId": 90, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 13, + "scoreDiff": 1.5, + "moveCount": 26, + "duration": 166 + }, + { + "gameId": 91, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 186 + }, + { + "gameId": 92, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 9, + "scoreDiff": 2.5, + "moveCount": 19, + "duration": 112 + }, + { + "gameId": 93, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 22, + "duration": 139 + }, + { + "gameId": 94, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 31, + "duration": 235 + }, + { + "gameId": 95, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 5.5, + "scoreDiff": 13.5, + "moveCount": 31, + "duration": 281 + }, + { + "gameId": 96, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 9, + "scoreDiff": -5.5, + "moveCount": 32, + "duration": 254 + }, + { + "gameId": 97, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 208 + }, + { + "gameId": 98, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 14, + "scoreDiff": 2.5, + "moveCount": 26, + "duration": 203 + }, + { + "gameId": 99, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 8.5, + "scoreDiff": 4.5, + "moveCount": 23, + "duration": 190 + }, + { + "gameId": 100, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 9, + "scoreDiff": -6.5, + "moveCount": 26, + "duration": 151 + }, + { + "gameId": 101, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 7.5, + "scoreDiff": 7.5, + "moveCount": 29, + "duration": 204 + }, + { + "gameId": 102, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 21.5, + "model2Score": 2, + "scoreDiff": -19.5, + "moveCount": 32, + "duration": 255 + }, + { + "gameId": 103, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 277 + }, + { + "gameId": 104, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 211 + }, + { + "gameId": 105, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 11.5, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 158 + }, + { + "gameId": 106, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 6, + "scoreDiff": -8.5, + "moveCount": 24, + "duration": 227 + }, + { + "gameId": 107, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 210 + }, + { + "gameId": 108, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 6, + "scoreDiff": -9.5, + "moveCount": 28, + "duration": 261 + }, + { + "gameId": 109, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 4.5, + "scoreDiff": 13.5, + "moveCount": 37, + "duration": 373 + }, + { + "gameId": 110, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 10, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 184 + }, + { + "gameId": 111, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 5.5, + "scoreDiff": -0.5, + "moveCount": 12, + "duration": 83 + }, + { + "gameId": 112, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 27, + "duration": 276 + }, + { + "gameId": 113, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 223 + }, + { + "gameId": 114, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 3, + "scoreDiff": -13.5, + "moveCount": 34, + "duration": 286 + }, + { + "gameId": 115, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 6.5, + "scoreDiff": 8.5, + "moveCount": 29, + "duration": 283 + }, + { + "gameId": 116, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 5, + "scoreDiff": -12.5, + "moveCount": 32, + "duration": 322 + }, + { + "gameId": 117, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 31, + "duration": 216 + }, + { + "gameId": 118, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 263 + }, + { + "gameId": 119, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 31, + "duration": 352 + }, + { + "gameId": 120, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 7, + "scoreDiff": -9.5, + "moveCount": 30, + "duration": 285 + }, + { + "gameId": 121, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 9.5, + "scoreDiff": 1.5, + "moveCount": 21, + "duration": 190 + }, + { + "gameId": 122, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 7, + "scoreDiff": -0.5, + "moveCount": 18, + "duration": 119 + }, + { + "gameId": 123, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 239 + }, + { + "gameId": 124, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 31, + "duration": 325 + }, + { + "gameId": 125, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 199 + }, + { + "gameId": 126, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 6, + "scoreDiff": -12.5, + "moveCount": 34, + "duration": 295 + }, + { + "gameId": 127, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 14.5, + "scoreDiff": -5.5, + "moveCount": 30, + "duration": 391 + }, + { + "gameId": 128, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 194 + }, + { + "gameId": 129, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 11.5, + "scoreDiff": -1.5, + "moveCount": 30, + "duration": 243 + }, + { + "gameId": 130, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 3, + "scoreDiff": -15.5, + "moveCount": 31, + "duration": 327 + }, + { + "gameId": 131, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 31, + "duration": 252 + }, + { + "gameId": 132, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 23, + "duration": 224 + }, + { + "gameId": 133, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 24, + "duration": 190 + }, + { + "gameId": 134, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 224 + }, + { + "gameId": 135, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 11.5, + "scoreDiff": -4.5, + "moveCount": 24, + "duration": 256 + }, + { + "gameId": 136, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 294 + }, + { + "gameId": 137, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 11.5, + "scoreDiff": -1.5, + "moveCount": 28, + "duration": 282 + }, + { + "gameId": 138, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 283 + }, + { + "gameId": 139, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 362 + }, + { + "gameId": 140, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 7, + "scoreDiff": -7.5, + "moveCount": 28, + "duration": 241 + }, + { + "gameId": 141, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 26, + "duration": 302 + }, + { + "gameId": 142, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 8, + "scoreDiff": -3.5, + "moveCount": 24, + "duration": 287 + }, + { + "gameId": 143, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 12.5, + "scoreDiff": -5.5, + "moveCount": 26, + "duration": 231 + }, + { + "gameId": 144, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 15, + "scoreDiff": 5.5, + "moveCount": 31, + "duration": 218 + }, + { + "gameId": 145, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 14.5, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 221 + }, + { + "gameId": 146, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 5, + "scoreDiff": -12.5, + "moveCount": 28, + "duration": 317 + }, + { + "gameId": 147, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 315 + }, + { + "gameId": 148, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 6, + "scoreDiff": -11.5, + "moveCount": 30, + "duration": 324 + }, + { + "gameId": 149, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 10, + "model2Score": 8.5, + "scoreDiff": 1.5, + "moveCount": 21, + "duration": 178 + }, + { + "gameId": 150, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 8, + "scoreDiff": -1.5, + "moveCount": 19, + "duration": 187 + }, + { + "gameId": 151, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 10, + "model2Score": 8.5, + "scoreDiff": 1.5, + "moveCount": 19, + "duration": 198 + }, + { + "gameId": 152, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 7, + "scoreDiff": -2.5, + "moveCount": 21, + "duration": 178 + }, + { + "gameId": 153, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 183 + }, + { + "gameId": 154, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 9, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 239 + }, + { + "gameId": 155, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 23, + "model2Score": 1.5, + "scoreDiff": 21.5, + "moveCount": 45, + "duration": 361 + }, + { + "gameId": 156, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 25, + "duration": 191 + }, + { + "gameId": 157, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 15.5, + "scoreDiff": -8.5, + "moveCount": 34, + "duration": 277 + }, + { + "gameId": 158, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 14, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 283 + }, + { + "gameId": 159, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 11.5, + "scoreDiff": -3.5, + "moveCount": 22, + "duration": 167 + }, + { + "gameId": 160, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 40, + "duration": 438 + }, + { + "gameId": 161, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 213 + }, + { + "gameId": 162, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 6, + "scoreDiff": -2.5, + "moveCount": 18, + "duration": 157 + }, + { + "gameId": 163, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 3.5, + "scoreDiff": 13.5, + "moveCount": 36, + "duration": 285 + }, + { + "gameId": 164, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 229 + }, + { + "gameId": 165, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 200 + }, + { + "gameId": 166, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 7, + "scoreDiff": -8.5, + "moveCount": 32, + "duration": 402 + }, + { + "gameId": 167, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 27, + "duration": 303 + }, + { + "gameId": 168, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 253 + }, + { + "gameId": 169, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 131 + }, + { + "gameId": 170, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 8, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 302 + }, + { + "gameId": 171, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 28, + "duration": 228 + }, + { + "gameId": 172, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 28, + "duration": 228 + }, + { + "gameId": 173, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 8.5, + "scoreDiff": 3.5, + "moveCount": 23, + "duration": 157 + }, + { + "gameId": 174, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 20, + "duration": 146 + }, + { + "gameId": 175, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 8.5, + "scoreDiff": 7.5, + "moveCount": 36, + "duration": 436 + }, + { + "gameId": 176, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 180 + }, + { + "gameId": 177, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 26, + "duration": 186 + }, + { + "gameId": 178, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 7, + "scoreDiff": -2.5, + "moveCount": 20, + "duration": 135 + }, + { + "gameId": 179, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 10.5, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 182 + }, + { + "gameId": 180, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 7, + "scoreDiff": -6.5, + "moveCount": 24, + "duration": 182 + }, + { + "gameId": 181, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 27, + "duration": 191 + }, + { + "gameId": 182, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 199 + }, + { + "gameId": 183, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 24, + "duration": 147 + }, + { + "gameId": 184, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 224 + }, + { + "gameId": 185, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 13.5, + "scoreDiff": -5.5, + "moveCount": 24, + "duration": 162 + }, + { + "gameId": 186, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 11, + "scoreDiff": 1.5, + "moveCount": 23, + "duration": 145 + }, + { + "gameId": 187, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 7.5, + "scoreDiff": 9.5, + "moveCount": 29, + "duration": 236 + }, + { + "gameId": 188, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 7, + "scoreDiff": -10.5, + "moveCount": 34, + "duration": 369 + }, + { + "gameId": 189, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 10.5, + "scoreDiff": -0.5, + "moveCount": 26, + "duration": 246 + }, + { + "gameId": 190, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 25, + "duration": 138 + }, + { + "gameId": 191, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 13.5, + "scoreDiff": -4.5, + "moveCount": 24, + "duration": 270 + }, + { + "gameId": 192, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 13, + "scoreDiff": 4.5, + "moveCount": 29, + "duration": 265 + }, + { + "gameId": 193, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 363 + }, + { + "gameId": 194, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 15, + "scoreDiff": 9.5, + "moveCount": 29, + "duration": 307 + }, + { + "gameId": 195, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 34, + "duration": 309 + }, + { + "gameId": 196, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 3, + "scoreDiff": -16.5, + "moveCount": 37, + "duration": 444 + }, + { + "gameId": 197, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 13.5, + "scoreDiff": -4.5, + "moveCount": 24, + "duration": 211 + }, + { + "gameId": 198, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 230 + }, + { + "gameId": 199, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 29, + "duration": 291 + }, + { + "gameId": 200, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 29, + "duration": 297 + }, + { + "gameId": 201, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 325 + }, + { + "gameId": 202, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 7, + "scoreDiff": -7.5, + "moveCount": 26, + "duration": 267 + }, + { + "gameId": 203, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 9.5, + "scoreDiff": 1.5, + "moveCount": 21, + "duration": 150 + }, + { + "gameId": 204, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 186 + }, + { + "gameId": 205, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 7.5, + "scoreDiff": 7.5, + "moveCount": 31, + "duration": 313 + }, + { + "gameId": 206, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 10, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 161 + }, + { + "gameId": 207, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 10, + "model2Score": 6.5, + "scoreDiff": 3.5, + "moveCount": 17, + "duration": 133 + }, + { + "gameId": 208, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 11, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 200 + }, + { + "gameId": 209, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 355 + }, + { + "gameId": 210, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 13, + "scoreDiff": 3.5, + "moveCount": 25, + "duration": 360 + }, + { + "gameId": 211, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 30, + "duration": 293 + }, + { + "gameId": 212, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 7, + "scoreDiff": -7.5, + "moveCount": 27, + "duration": 219 + }, + { + "gameId": 213, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 280 + }, + { + "gameId": 214, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 22.5, + "model2Score": 2, + "scoreDiff": -20.5, + "moveCount": 30, + "duration": 273 + }, + { + "gameId": 215, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 13.5, + "scoreDiff": -4.5, + "moveCount": 24, + "duration": 184 + }, + { + "gameId": 216, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 17, + "scoreDiff": 10.5, + "moveCount": 27, + "duration": 251 + }, + { + "gameId": 217, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 31, + "duration": 322 + }, + { + "gameId": 218, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 6, + "scoreDiff": -10.5, + "moveCount": 26, + "duration": 178 + }, + { + "gameId": 219, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 35, + "duration": 312 + }, + { + "gameId": 220, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 11, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 192 + }, + { + "gameId": 221, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 13.5, + "scoreDiff": -5.5, + "moveCount": 24, + "duration": 233 + }, + { + "gameId": 222, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 10, + "scoreDiff": 1.5, + "moveCount": 21, + "duration": 154 + }, + { + "gameId": 223, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 17.5, + "scoreDiff": -10.5, + "moveCount": 34, + "duration": 277 + }, + { + "gameId": 224, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 196 + }, + { + "gameId": 225, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 24, + "duration": 247 + }, + { + "gameId": 226, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 12, + "scoreDiff": 3.5, + "moveCount": 25, + "duration": 172 + }, + { + "gameId": 227, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 9.5, + "scoreDiff": 6.5, + "moveCount": 24, + "duration": 184 + }, + { + "gameId": 228, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 8, + "scoreDiff": -4.5, + "moveCount": 24, + "duration": 229 + }, + { + "gameId": 229, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 1.5, + "scoreDiff": 16.5, + "moveCount": 33, + "duration": 329 + }, + { + "gameId": 230, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 254 + }, + { + "gameId": 231, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 187 + }, + { + "gameId": 232, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 10, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 192 + }, + { + "gameId": 233, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 8.5, + "scoreDiff": 7.5, + "moveCount": 25, + "duration": 256 + }, + { + "gameId": 234, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 10, + "scoreDiff": 0.5, + "moveCount": 21, + "duration": 182 + }, + { + "gameId": 235, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 340 + }, + { + "gameId": 236, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 316 + }, + { + "gameId": 237, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 185 + }, + { + "gameId": 238, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 27, + "duration": 208 + }, + { + "gameId": 239, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 298 + }, + { + "gameId": 240, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 8, + "scoreDiff": 0.5, + "moveCount": 15, + "duration": 117 + }, + { + "gameId": 241, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 33, + "duration": 268 + }, + { + "gameId": 242, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 6, + "scoreDiff": -10.5, + "moveCount": 30, + "duration": 272 + }, + { + "gameId": 243, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 3.5, + "scoreDiff": 16.5, + "moveCount": 33, + "duration": 438 + }, + { + "gameId": 244, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 11, + "scoreDiff": 3.5, + "moveCount": 21, + "duration": 161 + }, + { + "gameId": 245, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 10, + "model2Score": 9.5, + "scoreDiff": 0.5, + "moveCount": 22, + "duration": 198 + }, + { + "gameId": 246, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 12, + "scoreDiff": 1.5, + "moveCount": 29, + "duration": 245 + }, + { + "gameId": 247, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 4, + "model2Score": 19.5, + "scoreDiff": -15.5, + "moveCount": 40, + "duration": 472 + }, + { + "gameId": 248, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 5, + "scoreDiff": -11.5, + "moveCount": 32, + "duration": 340 + }, + { + "gameId": 249, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 6.5, + "scoreDiff": 12.5, + "moveCount": 41, + "duration": 498 + }, + { + "gameId": 250, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 12, + "scoreDiff": -0.5, + "moveCount": 30, + "duration": 293 + }, + { + "gameId": 251, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 12.5, + "scoreDiff": -5.5, + "moveCount": 24, + "duration": 315 + }, + { + "gameId": 252, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 8, + "scoreDiff": -4.5, + "moveCount": 23, + "duration": 184 + }, + { + "gameId": 253, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 17.5, + "scoreDiff": -12.5, + "moveCount": 36, + "duration": 458 + }, + { + "gameId": 254, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 5, + "scoreDiff": -11.5, + "moveCount": 28, + "duration": 255 + }, + { + "gameId": 255, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 3.5, + "scoreDiff": 16.5, + "moveCount": 39, + "duration": 538 + }, + { + "gameId": 256, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 22.5, + "model2Score": 3, + "scoreDiff": -19.5, + "moveCount": 32, + "duration": 264 + }, + { + "gameId": 257, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 32, + "duration": 273 + }, + { + "gameId": 258, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 242 + }, + { + "gameId": 259, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 28, + "duration": 195 + }, + { + "gameId": 260, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 7, + "scoreDiff": 1.5, + "moveCount": 13, + "duration": 87 + }, + { + "gameId": 261, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 15.5, + "scoreDiff": -6.5, + "moveCount": 25, + "duration": 177 + }, + { + "gameId": 262, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 30, + "duration": 287 + }, + { + "gameId": 263, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 260 + }, + { + "gameId": 264, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 7, + "scoreDiff": -5.5, + "moveCount": 20, + "duration": 153 + }, + { + "gameId": 265, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 9.5, + "scoreDiff": 2.5, + "moveCount": 24, + "duration": 203 + }, + { + "gameId": 266, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 21, + "duration": 190 + }, + { + "gameId": 267, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 35, + "duration": 360 + }, + { + "gameId": 268, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 20.5, + "model2Score": 3, + "scoreDiff": -17.5, + "moveCount": 34, + "duration": 302 + }, + { + "gameId": 269, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 15.5, + "scoreDiff": -8.5, + "moveCount": 24, + "duration": 176 + }, + { + "gameId": 270, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 8, + "scoreDiff": -1.5, + "moveCount": 21, + "duration": 151 + }, + { + "gameId": 271, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 22, + "model2Score": 3.5, + "scoreDiff": 18.5, + "moveCount": 33, + "duration": 337 + }, + { + "gameId": 272, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 259 + }, + { + "gameId": 273, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 293 + }, + { + "gameId": 274, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 190 + }, + { + "gameId": 275, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 30, + "duration": 244 + }, + { + "gameId": 276, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 196 + }, + { + "gameId": 277, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 5.5, + "scoreDiff": 11.5, + "moveCount": 37, + "duration": 506 + }, + { + "gameId": 278, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 5, + "scoreDiff": -10.5, + "moveCount": 29, + "duration": 195 + }, + { + "gameId": 279, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 9.5, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 218 + }, + { + "gameId": 280, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 13, + "scoreDiff": 3.5, + "moveCount": 24, + "duration": 221 + }, + { + "gameId": 281, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 31, + "duration": 251 + }, + { + "gameId": 282, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 194 + }, + { + "gameId": 283, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 27, + "duration": 216 + }, + { + "gameId": 284, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 7, + "scoreDiff": -2.5, + "moveCount": 21, + "duration": 156 + }, + { + "gameId": 285, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 17.5, + "scoreDiff": -11.5, + "moveCount": 30, + "duration": 228 + }, + { + "gameId": 286, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 20.5, + "model2Score": 3, + "scoreDiff": -17.5, + "moveCount": 40, + "duration": 391 + }, + { + "gameId": 287, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 156 + }, + { + "gameId": 288, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 3, + "scoreDiff": -12.5, + "moveCount": 33, + "duration": 306 + }, + { + "gameId": 289, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 32, + "duration": 231 + }, + { + "gameId": 290, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 30, + "duration": 233 + }, + { + "gameId": 291, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 25, + "duration": 201 + }, + { + "gameId": 292, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 208 + }, + { + "gameId": 293, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 201 + }, + { + "gameId": 294, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 12, + "scoreDiff": 2.5, + "moveCount": 23, + "duration": 189 + }, + { + "gameId": 295, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 28, + "duration": 233 + }, + { + "gameId": 296, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 12, + "scoreDiff": 1.5, + "moveCount": 25, + "duration": 195 + }, + { + "gameId": 297, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 2.5, + "scoreDiff": 17.5, + "moveCount": 30, + "duration": 243 + }, + { + "gameId": 298, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 6, + "scoreDiff": -13.5, + "moveCount": 34, + "duration": 295 + }, + { + "gameId": 299, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 9.5, + "scoreDiff": 2.5, + "moveCount": 30, + "duration": 244 + }, + { + "gameId": 300, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 4.5, + "model2Score": 4, + "scoreDiff": -0.5, + "moveCount": 10, + "duration": 48 + }, + { + "gameId": 301, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 204 + }, + { + "gameId": 302, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 22.5, + "model2Score": 1, + "scoreDiff": -21.5, + "moveCount": 28, + "duration": 255 + }, + { + "gameId": 303, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 10, + "model2Score": 9.5, + "scoreDiff": 0.5, + "moveCount": 21, + "duration": 184 + }, + { + "gameId": 304, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 193 + }, + { + "gameId": 305, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 203 + }, + { + "gameId": 306, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 17, + "scoreDiff": 10.5, + "moveCount": 31, + "duration": 236 + }, + { + "gameId": 307, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 26, + "duration": 183 + }, + { + "gameId": 308, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 8, + "scoreDiff": -5.5, + "moveCount": 26, + "duration": 215 + }, + { + "gameId": 309, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 9.5, + "scoreDiff": -1.5, + "moveCount": 19, + "duration": 117 + }, + { + "gameId": 310, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 14, + "scoreDiff": 3.5, + "moveCount": 31, + "duration": 241 + }, + { + "gameId": 311, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 186 + }, + { + "gameId": 312, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 7, + "scoreDiff": -2.5, + "moveCount": 19, + "duration": 106 + }, + { + "gameId": 313, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 16.5, + "scoreDiff": -10.5, + "moveCount": 26, + "duration": 191 + }, + { + "gameId": 314, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 34, + "duration": 317 + }, + { + "gameId": 315, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 249 + }, + { + "gameId": 316, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 27, + "duration": 211 + }, + { + "gameId": 317, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 4, + "model2Score": 18.5, + "scoreDiff": -14.5, + "moveCount": 34, + "duration": 266 + }, + { + "gameId": 318, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 7, + "scoreDiff": -9.5, + "moveCount": 32, + "duration": 418 + }, + { + "gameId": 319, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 230 + }, + { + "gameId": 320, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 23, + "duration": 138 + }, + { + "gameId": 321, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 14.5, + "scoreDiff": -6.5, + "moveCount": 30, + "duration": 253 + }, + { + "gameId": 322, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 7, + "scoreDiff": -5.5, + "moveCount": 23, + "duration": 174 + }, + { + "gameId": 323, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 28, + "duration": 261 + }, + { + "gameId": 324, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 8, + "scoreDiff": -8.5, + "moveCount": 36, + "duration": 358 + }, + { + "gameId": 325, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 26, + "duration": 206 + }, + { + "gameId": 326, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 286 + }, + { + "gameId": 327, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 277 + }, + { + "gameId": 328, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 264 + }, + { + "gameId": 329, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 264 + }, + { + "gameId": 330, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 23, + "duration": 164 + }, + { + "gameId": 331, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 9, + "model2Score": 8.5, + "scoreDiff": 0.5, + "moveCount": 24, + "duration": 161 + }, + { + "gameId": 332, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 16, + "scoreDiff": 7.5, + "moveCount": 27, + "duration": 206 + }, + { + "gameId": 333, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 8.5, + "scoreDiff": 7.5, + "moveCount": 27, + "duration": 192 + }, + { + "gameId": 334, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 13, + "scoreDiff": 1.5, + "moveCount": 28, + "duration": 217 + }, + { + "gameId": 335, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 9.5, + "scoreDiff": -4.5, + "moveCount": 18, + "duration": 139 + }, + { + "gameId": 336, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 225 + }, + { + "gameId": 337, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 234 + }, + { + "gameId": 338, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 23, + "duration": 139 + }, + { + "gameId": 339, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 21, + "model2Score": 3.5, + "scoreDiff": 17.5, + "moveCount": 35, + "duration": 271 + }, + { + "gameId": 340, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 31, + "duration": 308 + }, + { + "gameId": 341, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 31, + "duration": 254 + }, + { + "gameId": 342, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 7, + "scoreDiff": -3.5, + "moveCount": 20, + "duration": 163 + }, + { + "gameId": 343, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 15.5, + "scoreDiff": -6.5, + "moveCount": 30, + "duration": 353 + }, + { + "gameId": 344, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 9, + "scoreDiff": -5.5, + "moveCount": 30, + "duration": 324 + }, + { + "gameId": 345, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 33, + "duration": 306 + }, + { + "gameId": 346, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 7, + "scoreDiff": -9.5, + "moveCount": 28, + "duration": 243 + }, + { + "gameId": 347, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 10.5, + "scoreDiff": -1.5, + "moveCount": 20, + "duration": 208 + }, + { + "gameId": 348, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 13, + "scoreDiff": 4.5, + "moveCount": 24, + "duration": 219 + }, + { + "gameId": 349, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 4, + "model2Score": 15.5, + "scoreDiff": -11.5, + "moveCount": 28, + "duration": 217 + }, + { + "gameId": 350, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 11, + "scoreDiff": 1.5, + "moveCount": 21, + "duration": 136 + }, + { + "gameId": 351, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 260 + }, + { + "gameId": 352, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 24, + "duration": 164 + }, + { + "gameId": 353, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 26, + "duration": 164 + }, + { + "gameId": 354, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 20.5, + "model2Score": 4, + "scoreDiff": -16.5, + "moveCount": 36, + "duration": 338 + }, + { + "gameId": 355, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 7.5, + "scoreDiff": -1.5, + "moveCount": 15, + "duration": 86 + }, + { + "gameId": 356, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 6, + "scoreDiff": -10.5, + "moveCount": 30, + "duration": 299 + }, + { + "gameId": 357, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 211 + }, + { + "gameId": 358, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 7, + "scoreDiff": -3.5, + "moveCount": 21, + "duration": 207 + }, + { + "gameId": 359, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 11.5, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 166 + }, + { + "gameId": 360, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 9, + "scoreDiff": -6.5, + "moveCount": 24, + "duration": 245 + }, + { + "gameId": 361, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 10, + "model2Score": 8.5, + "scoreDiff": 1.5, + "moveCount": 19, + "duration": 186 + }, + { + "gameId": 362, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 214 + }, + { + "gameId": 363, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 15.5, + "scoreDiff": -6.5, + "moveCount": 26, + "duration": 207 + }, + { + "gameId": 364, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 5, + "scoreDiff": -7.5, + "moveCount": 21, + "duration": 151 + }, + { + "gameId": 365, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 226 + }, + { + "gameId": 366, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 2.5, + "model2Score": 2, + "scoreDiff": -0.5, + "moveCount": 6, + "duration": 34 + }, + { + "gameId": 367, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 10, + "model2Score": 8.5, + "scoreDiff": 1.5, + "moveCount": 20, + "duration": 133 + }, + { + "gameId": 368, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 2.5, + "model2Score": 17, + "scoreDiff": 14.5, + "moveCount": 33, + "duration": 266 + }, + { + "gameId": 369, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 8.5, + "scoreDiff": 4.5, + "moveCount": 26, + "duration": 210 + }, + { + "gameId": 370, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 6, + "scoreDiff": -10.5, + "moveCount": 36, + "duration": 283 + }, + { + "gameId": 371, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 10.5, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 188 + }, + { + "gameId": 372, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 7, + "scoreDiff": -3.5, + "moveCount": 22, + "duration": 158 + }, + { + "gameId": 373, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 5.5, + "scoreDiff": 6.5, + "moveCount": 28, + "duration": 181 + }, + { + "gameId": 374, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 20.5, + "model2Score": 2, + "scoreDiff": -18.5, + "moveCount": 33, + "duration": 279 + }, + { + "gameId": 375, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 12.5, + "scoreDiff": -4.5, + "moveCount": 32, + "duration": 305 + }, + { + "gameId": 376, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 24, + "duration": 193 + }, + { + "gameId": 377, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 7.5, + "scoreDiff": 3.5, + "moveCount": 21, + "duration": 224 + }, + { + "gameId": 378, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 223 + }, + { + "gameId": 379, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 22, + "duration": 159 + }, + { + "gameId": 380, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 8, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 252 + }, + { + "gameId": 381, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 24, + "duration": 254 + }, + { + "gameId": 382, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 169 + }, + { + "gameId": 383, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 22, + "model2Score": 2.5, + "scoreDiff": 19.5, + "moveCount": 41, + "duration": 481 + }, + { + "gameId": 384, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 6.5, + "model2Score": 4, + "scoreDiff": -2.5, + "moveCount": 13, + "duration": 102 + }, + { + "gameId": 385, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 4.5, + "scoreDiff": 9.5, + "moveCount": 27, + "duration": 296 + }, + { + "gameId": 386, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 27, + "duration": 201 + }, + { + "gameId": 387, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 4.5, + "scoreDiff": 15.5, + "moveCount": 37, + "duration": 322 + }, + { + "gameId": 388, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 8, + "scoreDiff": 0.5, + "moveCount": 17, + "duration": 112 + }, + { + "gameId": 389, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 199 + }, + { + "gameId": 390, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 311 + }, + { + "gameId": 391, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 284 + }, + { + "gameId": 392, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 4, + "scoreDiff": -11.5, + "moveCount": 35, + "duration": 422 + }, + { + "gameId": 393, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 30, + "duration": 284 + }, + { + "gameId": 394, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 235 + }, + { + "gameId": 395, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 210 + }, + { + "gameId": 396, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 15, + "scoreDiff": 7.5, + "moveCount": 37, + "duration": 331 + }, + { + "gameId": 397, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 22, + "model2Score": 2.5, + "scoreDiff": 19.5, + "moveCount": 39, + "duration": 424 + }, + { + "gameId": 398, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 286 + }, + { + "gameId": 399, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 12.5, + "scoreDiff": -3.5, + "moveCount": 33, + "duration": 329 + }, + { + "gameId": 400, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 5, + "scoreDiff": -13.5, + "moveCount": 32, + "duration": 271 + }, + { + "gameId": 401, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 10.5, + "scoreDiff": 4.5, + "moveCount": 38, + "duration": 423 + }, + { + "gameId": 402, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 4, + "scoreDiff": -15.5, + "moveCount": 38, + "duration": 511 + }, + { + "gameId": 403, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 200 + }, + { + "gameId": 404, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 324 + }, + { + "gameId": 405, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 16.5, + "scoreDiff": -11.5, + "moveCount": 30, + "duration": 302 + }, + { + "gameId": 406, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 9, + "scoreDiff": -6.5, + "moveCount": 30, + "duration": 287 + }, + { + "gameId": 407, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 271 + }, + { + "gameId": 408, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 17, + "scoreDiff": 9.5, + "moveCount": 25, + "duration": 207 + }, + { + "gameId": 409, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 6.5, + "scoreDiff": 9.5, + "moveCount": 31, + "duration": 329 + }, + { + "gameId": 410, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 19, + "duration": 107 + }, + { + "gameId": 411, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 16.5, + "scoreDiff": -9.5, + "moveCount": 28, + "duration": 220 + }, + { + "gameId": 412, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 11, + "scoreDiff": 1.5, + "moveCount": 25, + "duration": 315 + }, + { + "gameId": 413, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 30, + "duration": 264 + }, + { + "gameId": 414, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 148 + }, + { + "gameId": 415, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 9.5, + "scoreDiff": -1.5, + "moveCount": 22, + "duration": 178 + }, + { + "gameId": 416, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 14, + "scoreDiff": 3.5, + "moveCount": 34, + "duration": 295 + }, + { + "gameId": 417, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 10.5, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 164 + }, + { + "gameId": 418, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 214 + }, + { + "gameId": 419, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 32, + "duration": 258 + }, + { + "gameId": 420, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 13, + "scoreDiff": 3.5, + "moveCount": 23, + "duration": 154 + }, + { + "gameId": 421, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 7.5, + "scoreDiff": 6.5, + "moveCount": 29, + "duration": 298 + }, + { + "gameId": 422, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 7, + "scoreDiff": -9.5, + "moveCount": 28, + "duration": 229 + }, + { + "gameId": 423, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 205 + }, + { + "gameId": 424, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 27, + "duration": 226 + }, + { + "gameId": 425, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 33, + "duration": 288 + }, + { + "gameId": 426, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 9, + "scoreDiff": -6.5, + "moveCount": 31, + "duration": 259 + }, + { + "gameId": 427, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 255 + }, + { + "gameId": 428, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 9, + "scoreDiff": -5.5, + "moveCount": 30, + "duration": 304 + }, + { + "gameId": 429, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 134 + }, + { + "gameId": 430, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 7, + "scoreDiff": -6.5, + "moveCount": 26, + "duration": 181 + }, + { + "gameId": 431, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 14.5, + "scoreDiff": -8.5, + "moveCount": 28, + "duration": 195 + }, + { + "gameId": 432, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 244 + }, + { + "gameId": 433, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 23, + "duration": 165 + }, + { + "gameId": 434, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 15, + "scoreDiff": 5.5, + "moveCount": 29, + "duration": 243 + }, + { + "gameId": 435, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 24, + "duration": 147 + }, + { + "gameId": 436, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 192 + }, + { + "gameId": 437, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 15.5, + "scoreDiff": -5.5, + "moveCount": 29, + "duration": 200 + }, + { + "gameId": 438, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 8, + "scoreDiff": -1.5, + "moveCount": 20, + "duration": 125 + }, + { + "gameId": 439, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 11.5, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 219 + }, + { + "gameId": 440, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 6, + "scoreDiff": -8.5, + "moveCount": 26, + "duration": 199 + }, + { + "gameId": 441, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 25, + "duration": 185 + }, + { + "gameId": 442, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 23, + "duration": 158 + }, + { + "gameId": 443, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 184 + }, + { + "gameId": 444, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 16, + "scoreDiff": 7.5, + "moveCount": 27, + "duration": 196 + }, + { + "gameId": 445, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 14.5, + "scoreDiff": -9.5, + "moveCount": 34, + "duration": 289 + }, + { + "gameId": 446, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 6, + "scoreDiff": -9.5, + "moveCount": 32, + "duration": 273 + }, + { + "gameId": 447, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 195 + }, + { + "gameId": 448, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 7, + "scoreDiff": -1.5, + "moveCount": 22, + "duration": 158 + }, + { + "gameId": 449, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 9.5, + "scoreDiff": 1.5, + "moveCount": 24, + "duration": 181 + }, + { + "gameId": 450, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 31, + "duration": 244 + }, + { + "gameId": 451, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 8.5, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 186 + }, + { + "gameId": 452, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 253 + }, + { + "gameId": 453, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 24, + "duration": 210 + }, + { + "gameId": 454, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 7, + "scoreDiff": -3.5, + "moveCount": 22, + "duration": 169 + }, + { + "gameId": 455, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 1, + "model2Score": 23.5, + "scoreDiff": -22.5, + "moveCount": 44, + "duration": 405 + }, + { + "gameId": 456, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 13, + "scoreDiff": 4.5, + "moveCount": 23, + "duration": 169 + }, + { + "gameId": 457, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 286 + }, + { + "gameId": 458, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 9, + "scoreDiff": 0.5, + "moveCount": 19, + "duration": 142 + }, + { + "gameId": 459, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 9.5, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 207 + }, + { + "gameId": 460, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 12, + "scoreDiff": 1.5, + "moveCount": 25, + "duration": 190 + }, + { + "gameId": 461, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 201 + }, + { + "gameId": 462, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 6, + "scoreDiff": -1.5, + "moveCount": 17, + "duration": 131 + }, + { + "gameId": 463, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 29, + "duration": 287 + }, + { + "gameId": 464, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 239 + }, + { + "gameId": 465, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 14.5, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 196 + }, + { + "gameId": 466, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 7, + "scoreDiff": -9.5, + "moveCount": 28, + "duration": 315 + }, + { + "gameId": 467, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 33, + "duration": 402 + }, + { + "gameId": 468, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 7, + "scoreDiff": -1.5, + "moveCount": 18, + "duration": 135 + }, + { + "gameId": 469, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 11.5, + "scoreDiff": -3.5, + "moveCount": 21, + "duration": 198 + }, + { + "gameId": 470, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 7, + "scoreDiff": -4.5, + "moveCount": 21, + "duration": 151 + }, + { + "gameId": 471, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 29, + "duration": 211 + }, + { + "gameId": 472, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 8, + "scoreDiff": -5.5, + "moveCount": 26, + "duration": 191 + }, + { + "gameId": 473, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 10.5, + "scoreDiff": -1.5, + "moveCount": 22, + "duration": 164 + }, + { + "gameId": 474, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 24, + "duration": 185 + }, + { + "gameId": 475, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 9.5, + "scoreDiff": 6.5, + "moveCount": 25, + "duration": 176 + }, + { + "gameId": 476, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 14, + "scoreDiff": 6.5, + "moveCount": 25, + "duration": 182 + }, + { + "gameId": 477, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 26, + "duration": 197 + }, + { + "gameId": 478, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 4, + "scoreDiff": -15.5, + "moveCount": 38, + "duration": 303 + }, + { + "gameId": 479, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 12.5, + "scoreDiff": -7.5, + "moveCount": 30, + "duration": 227 + }, + { + "gameId": 480, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 12, + "scoreDiff": 3.5, + "moveCount": 24, + "duration": 240 + }, + { + "gameId": 481, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 29, + "duration": 327 + }, + { + "gameId": 482, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 29, + "duration": 314 + }, + { + "gameId": 483, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 9.5, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 255 + }, + { + "gameId": 484, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 20.5, + "model2Score": 2, + "scoreDiff": -18.5, + "moveCount": 34, + "duration": 474 + }, + { + "gameId": 485, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 9.5, + "scoreDiff": 2.5, + "moveCount": 24, + "duration": 163 + }, + { + "gameId": 486, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 7, + "scoreDiff": -10.5, + "moveCount": 36, + "duration": 460 + }, + { + "gameId": 487, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 333 + }, + { + "gameId": 488, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 8, + "scoreDiff": -3.5, + "moveCount": 22, + "duration": 205 + }, + { + "gameId": 489, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 234 + }, + { + "gameId": 490, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 8, + "scoreDiff": -0.5, + "moveCount": 18, + "duration": 137 + }, + { + "gameId": 491, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 29, + "duration": 315 + }, + { + "gameId": 492, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 34, + "duration": 293 + }, + { + "gameId": 493, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 11.5, + "scoreDiff": -5.5, + "moveCount": 24, + "duration": 179 + }, + { + "gameId": 494, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 239 + }, + { + "gameId": 495, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 26, + "duration": 239 + }, + { + "gameId": 496, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 29, + "duration": 455 + }, + { + "gameId": 497, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 267 + }, + { + "gameId": 498, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 9, + "scoreDiff": -1.5, + "moveCount": 23, + "duration": 282 + }, + { + "gameId": 499, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 10, + "model2Score": 6.5, + "scoreDiff": 3.5, + "moveCount": 14, + "duration": 95 + }, + { + "gameId": 500, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 22, + "duration": 158 + }, + { + "gameId": 501, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 21, + "duration": 128 + }, + { + "gameId": 502, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 14, + "scoreDiff": 6.5, + "moveCount": 25, + "duration": 302 + }, + { + "gameId": 503, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 326 + }, + { + "gameId": 504, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 23, + "duration": 162 + }, + { + "gameId": 505, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 4.5, + "scoreDiff": 14.5, + "moveCount": 35, + "duration": 283 + }, + { + "gameId": 506, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 296 + }, + { + "gameId": 507, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 12.5, + "scoreDiff": -3.5, + "moveCount": 27, + "duration": 297 + }, + { + "gameId": 508, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 20.5, + "model2Score": 3, + "scoreDiff": -17.5, + "moveCount": 30, + "duration": 295 + }, + { + "gameId": 509, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 35, + "duration": 407 + }, + { + "gameId": 510, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 29, + "duration": 232 + }, + { + "gameId": 511, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 29, + "duration": 253 + }, + { + "gameId": 512, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 188 + }, + { + "gameId": 513, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 289 + }, + { + "gameId": 514, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 15, + "scoreDiff": 7.5, + "moveCount": 23, + "duration": 231 + }, + { + "gameId": 515, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 227 + }, + { + "gameId": 516, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 7, + "scoreDiff": -2.5, + "moveCount": 21, + "duration": 131 + }, + { + "gameId": 517, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 17.5, + "scoreDiff": -11.5, + "moveCount": 27, + "duration": 326 + }, + { + "gameId": 518, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 25, + "duration": 203 + }, + { + "gameId": 519, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 12.5, + "scoreDiff": -4.5, + "moveCount": 27, + "duration": 271 + }, + { + "gameId": 520, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 11, + "scoreDiff": 1.5, + "moveCount": 21, + "duration": 191 + }, + { + "gameId": 521, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 12.5, + "scoreDiff": -3.5, + "moveCount": 24, + "duration": 170 + }, + { + "gameId": 522, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 6, + "scoreDiff": -8.5, + "moveCount": 30, + "duration": 214 + }, + { + "gameId": 523, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 28, + "duration": 223 + }, + { + "gameId": 524, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 8, + "scoreDiff": 0.5, + "moveCount": 15, + "duration": 112 + }, + { + "gameId": 525, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 30, + "duration": 307 + }, + { + "gameId": 526, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 341 + }, + { + "gameId": 527, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 227 + }, + { + "gameId": 528, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 413 + }, + { + "gameId": 529, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 10.5, + "scoreDiff": -0.5, + "moveCount": 22, + "duration": 163 + }, + { + "gameId": 530, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 2.5, + "model2Score": 21, + "scoreDiff": 18.5, + "moveCount": 35, + "duration": 308 + }, + { + "gameId": 531, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 178 + }, + { + "gameId": 532, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 264 + }, + { + "gameId": 533, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 277 + }, + { + "gameId": 534, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 19, + "scoreDiff": 14.5, + "moveCount": 35, + "duration": 461 + }, + { + "gameId": 535, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 31, + "duration": 335 + }, + { + "gameId": 536, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 235 + }, + { + "gameId": 537, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 12.5, + "scoreDiff": -6.5, + "moveCount": 26, + "duration": 196 + }, + { + "gameId": 538, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 29, + "duration": 270 + }, + { + "gameId": 539, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 5.5, + "scoreDiff": 10.5, + "moveCount": 33, + "duration": 278 + }, + { + "gameId": 540, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 6, + "scoreDiff": -7.5, + "moveCount": 24, + "duration": 127 + }, + { + "gameId": 541, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 195 + }, + { + "gameId": 542, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 25, + "duration": 196 + }, + { + "gameId": 543, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 25, + "duration": 216 + }, + { + "gameId": 544, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 279 + }, + { + "gameId": 545, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 27, + "duration": 246 + }, + { + "gameId": 546, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 326 + }, + { + "gameId": 547, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 24, + "duration": 217 + }, + { + "gameId": 548, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 7, + "scoreDiff": -4.5, + "moveCount": 25, + "duration": 269 + }, + { + "gameId": 549, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 34, + "duration": 342 + }, + { + "gameId": 550, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 5, + "scoreDiff": -11.5, + "moveCount": 28, + "duration": 232 + }, + { + "gameId": 551, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 25, + "duration": 185 + }, + { + "gameId": 552, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 30, + "duration": 411 + }, + { + "gameId": 553, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 11.5, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 256 + }, + { + "gameId": 554, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 7, + "scoreDiff": -11.5, + "moveCount": 36, + "duration": 375 + }, + { + "gameId": 555, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 181 + }, + { + "gameId": 556, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 21, + "duration": 144 + }, + { + "gameId": 557, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 7.5, + "scoreDiff": 5.5, + "moveCount": 21, + "duration": 212 + }, + { + "gameId": 558, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 271 + }, + { + "gameId": 559, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 232 + }, + { + "gameId": 560, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 14, + "scoreDiff": 5.5, + "moveCount": 29, + "duration": 234 + }, + { + "gameId": 561, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 33, + "duration": 338 + }, + { + "gameId": 562, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 8, + "scoreDiff": -8.5, + "moveCount": 26, + "duration": 244 + }, + { + "gameId": 563, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 302 + }, + { + "gameId": 564, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 30, + "duration": 282 + }, + { + "gameId": 565, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 26, + "duration": 245 + }, + { + "gameId": 566, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 15, + "scoreDiff": 4.5, + "moveCount": 30, + "duration": 292 + }, + { + "gameId": 567, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 9.5, + "scoreDiff": -0.5, + "moveCount": 20, + "duration": 269 + }, + { + "gameId": 568, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 17, + "scoreDiff": 9.5, + "moveCount": 37, + "duration": 389 + }, + { + "gameId": 569, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 4.5, + "scoreDiff": 14.5, + "moveCount": 36, + "duration": 343 + }, + { + "gameId": 570, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 12, + "scoreDiff": 2.5, + "moveCount": 23, + "duration": 178 + }, + { + "gameId": 571, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 6.5, + "scoreDiff": -0.5, + "moveCount": 16, + "duration": 89 + }, + { + "gameId": 572, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 2, + "scoreDiff": -17.5, + "moveCount": 40, + "duration": 369 + }, + { + "gameId": 573, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 169 + }, + { + "gameId": 574, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 171 + }, + { + "gameId": 575, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 14.5, + "scoreDiff": -6.5, + "moveCount": 22, + "duration": 132 + }, + { + "gameId": 576, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 28, + "duration": 253 + }, + { + "gameId": 577, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 4.5, + "scoreDiff": 12.5, + "moveCount": 29, + "duration": 219 + }, + { + "gameId": 578, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 8, + "scoreDiff": -3.5, + "moveCount": 21, + "duration": 144 + }, + { + "gameId": 579, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 13.5, + "scoreDiff": -6.5, + "moveCount": 31, + "duration": 347 + }, + { + "gameId": 580, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 8, + "scoreDiff": -8.5, + "moveCount": 28, + "duration": 359 + }, + { + "gameId": 581, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 29, + "duration": 284 + }, + { + "gameId": 582, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 27, + "duration": 212 + }, + { + "gameId": 583, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 251 + }, + { + "gameId": 584, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 5, + "scoreDiff": -10.5, + "moveCount": 26, + "duration": 191 + }, + { + "gameId": 585, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 23, + "model2Score": 1.5, + "scoreDiff": 21.5, + "moveCount": 27, + "duration": 220 + }, + { + "gameId": 586, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 6, + "scoreDiff": -10.5, + "moveCount": 30, + "duration": 303 + }, + { + "gameId": 587, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 29, + "duration": 198 + }, + { + "gameId": 588, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 22.5, + "model2Score": 2, + "scoreDiff": -20.5, + "moveCount": 34, + "duration": 277 + }, + { + "gameId": 589, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 16.5, + "scoreDiff": -10.5, + "moveCount": 30, + "duration": 264 + }, + { + "gameId": 590, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 25, + "duration": 212 + }, + { + "gameId": 591, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 10.5, + "scoreDiff": 4.5, + "moveCount": 35, + "duration": 338 + }, + { + "gameId": 592, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 175 + }, + { + "gameId": 593, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 29, + "duration": 264 + }, + { + "gameId": 594, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 9, + "scoreDiff": -6.5, + "moveCount": 32, + "duration": 261 + }, + { + "gameId": 595, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 30, + "duration": 247 + }, + { + "gameId": 596, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 8, + "scoreDiff": -5.5, + "moveCount": 28, + "duration": 255 + }, + { + "gameId": 597, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 13.5, + "scoreDiff": -4.5, + "moveCount": 24, + "duration": 188 + }, + { + "gameId": 598, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 8, + "scoreDiff": -8.5, + "moveCount": 32, + "duration": 296 + }, + { + "gameId": 599, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 12.5, + "scoreDiff": 0.5, + "moveCount": 29, + "duration": 218 + }, + { + "gameId": 600, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 237 + }, + { + "gameId": 601, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 268 + }, + { + "gameId": 602, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 8, + "scoreDiff": -5.5, + "moveCount": 26, + "duration": 283 + }, + { + "gameId": 603, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 27, + "duration": 378 + }, + { + "gameId": 604, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 6, + "scoreDiff": -11.5, + "moveCount": 32, + "duration": 332 + }, + { + "gameId": 605, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 29, + "duration": 300 + }, + { + "gameId": 606, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 30, + "duration": 277 + }, + { + "gameId": 607, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 35, + "duration": 259 + }, + { + "gameId": 608, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 14, + "scoreDiff": 2.5, + "moveCount": 28, + "duration": 229 + }, + { + "gameId": 609, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 39, + "duration": 424 + }, + { + "gameId": 610, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 12, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 169 + }, + { + "gameId": 611, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 24, + "duration": 164 + }, + { + "gameId": 612, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 19, + "duration": 229 + }, + { + "gameId": 613, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 31, + "duration": 358 + }, + { + "gameId": 614, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 238 + }, + { + "gameId": 615, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 10.5, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 196 + }, + { + "gameId": 616, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 5.5, + "model2Score": 5, + "scoreDiff": -0.5, + "moveCount": 13, + "duration": 122 + }, + { + "gameId": 617, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 12.5, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 238 + }, + { + "gameId": 618, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 7, + "scoreDiff": -6.5, + "moveCount": 28, + "duration": 224 + }, + { + "gameId": 619, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 192 + }, + { + "gameId": 620, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 40, + "duration": 414 + }, + { + "gameId": 621, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 11.5, + "scoreDiff": -1.5, + "moveCount": 24, + "duration": 264 + }, + { + "gameId": 622, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 40, + "duration": 487 + }, + { + "gameId": 623, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 4.5, + "scoreDiff": 8.5, + "moveCount": 32, + "duration": 241 + }, + { + "gameId": 624, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 29, + "duration": 241 + }, + { + "gameId": 625, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 6.5, + "scoreDiff": 11.5, + "moveCount": 35, + "duration": 317 + }, + { + "gameId": 626, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 29, + "duration": 302 + }, + { + "gameId": 627, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 23, + "duration": 196 + }, + { + "gameId": 628, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 309 + }, + { + "gameId": 629, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 13.5, + "scoreDiff": -5.5, + "moveCount": 26, + "duration": 316 + }, + { + "gameId": 630, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 24, + "duration": 294 + }, + { + "gameId": 631, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 12.5, + "scoreDiff": -4.5, + "moveCount": 23, + "duration": 184 + }, + { + "gameId": 632, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 3.5, + "model2Score": 16, + "scoreDiff": 12.5, + "moveCount": 31, + "duration": 276 + }, + { + "gameId": 633, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 24, + "duration": 219 + }, + { + "gameId": 634, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 8, + "scoreDiff": -4.5, + "moveCount": 27, + "duration": 252 + }, + { + "gameId": 635, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 8.5, + "scoreDiff": 2.5, + "moveCount": 25, + "duration": 188 + }, + { + "gameId": 636, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 7, + "scoreDiff": -11.5, + "moveCount": 32, + "duration": 411 + }, + { + "gameId": 637, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 28, + "duration": 277 + }, + { + "gameId": 638, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 13, + "scoreDiff": 6.5, + "moveCount": 21, + "duration": 153 + }, + { + "gameId": 639, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 11.5, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 222 + }, + { + "gameId": 640, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 9, + "scoreDiff": -5.5, + "moveCount": 26, + "duration": 299 + }, + { + "gameId": 641, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 5.5, + "scoreDiff": 8.5, + "moveCount": 38, + "duration": 417 + }, + { + "gameId": 642, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 5, + "scoreDiff": -13.5, + "moveCount": 34, + "duration": 378 + }, + { + "gameId": 643, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 27, + "duration": 249 + }, + { + "gameId": 644, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 15, + "scoreDiff": 5.5, + "moveCount": 27, + "duration": 223 + }, + { + "gameId": 645, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 14.5, + "scoreDiff": -7.5, + "moveCount": 28, + "duration": 250 + }, + { + "gameId": 646, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 7, + "scoreDiff": -7.5, + "moveCount": 28, + "duration": 260 + }, + { + "gameId": 647, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 7.5, + "scoreDiff": 7.5, + "moveCount": 29, + "duration": 206 + }, + { + "gameId": 648, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 25, + "duration": 185 + }, + { + "gameId": 649, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 6.5, + "scoreDiff": 6.5, + "moveCount": 26, + "duration": 213 + }, + { + "gameId": 650, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 30, + "duration": 228 + }, + { + "gameId": 651, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 13.5, + "scoreDiff": -7.5, + "moveCount": 22, + "duration": 211 + }, + { + "gameId": 652, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 252 + }, + { + "gameId": 653, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 214 + }, + { + "gameId": 654, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 13, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 280 + }, + { + "gameId": 655, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 32, + "duration": 373 + }, + { + "gameId": 656, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 6.5, + "model2Score": 6, + "scoreDiff": -0.5, + "moveCount": 15, + "duration": 111 + }, + { + "gameId": 657, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 15.5, + "scoreDiff": -7.5, + "moveCount": 28, + "duration": 221 + }, + { + "gameId": 658, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 5, + "scoreDiff": -12.5, + "moveCount": 32, + "duration": 306 + }, + { + "gameId": 659, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 7.5, + "scoreDiff": 7.5, + "moveCount": 29, + "duration": 209 + }, + { + "gameId": 660, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 16, + "scoreDiff": 8.5, + "moveCount": 25, + "duration": 164 + }, + { + "gameId": 661, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 13.5, + "scoreDiff": -1.5, + "moveCount": 34, + "duration": 279 + }, + { + "gameId": 662, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 342 + }, + { + "gameId": 663, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 4.5, + "scoreDiff": 13.5, + "moveCount": 31, + "duration": 281 + }, + { + "gameId": 664, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 12.5, + "model2Score": 13, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 235 + }, + { + "gameId": 665, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 176 + }, + { + "gameId": 666, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 7, + "scoreDiff": -5.5, + "moveCount": 21, + "duration": 231 + }, + { + "gameId": 667, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 300 + }, + { + "gameId": 668, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 18, + "scoreDiff": 12.5, + "moveCount": 29, + "duration": 259 + }, + { + "gameId": 669, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 6.5, + "scoreDiff": 7.5, + "moveCount": 34, + "duration": 330 + }, + { + "gameId": 670, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 32, + "duration": 294 + }, + { + "gameId": 671, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 171 + }, + { + "gameId": 672, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 34, + "duration": 313 + }, + { + "gameId": 673, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 4.5, + "scoreDiff": 14.5, + "moveCount": 37, + "duration": 337 + }, + { + "gameId": 674, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 13, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 232 + }, + { + "gameId": 675, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 8.5, + "scoreDiff": 4.5, + "moveCount": 26, + "duration": 126 + }, + { + "gameId": 676, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 5, + "scoreDiff": -9.5, + "moveCount": 28, + "duration": 149 + }, + { + "gameId": 677, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 24, + "duration": 122 + }, + { + "gameId": 678, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 7, + "scoreDiff": -7.5, + "moveCount": 28, + "duration": 135 + }, + { + "gameId": 679, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 25, + "duration": 93 + }, + { + "gameId": 680, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 7, + "scoreDiff": -8.5, + "moveCount": 30, + "duration": 125 + }, + { + "gameId": 681, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 26, + "duration": 101 + }, + { + "gameId": 682, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 19, + "duration": 73 + }, + { + "gameId": 683, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 27, + "duration": 121 + }, + { + "gameId": 684, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 2.5, + "model2Score": 20, + "scoreDiff": 17.5, + "moveCount": 29, + "duration": 115 + }, + { + "gameId": 685, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 31, + "duration": 132 + }, + { + "gameId": 686, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 9, + "scoreDiff": -1.5, + "moveCount": 22, + "duration": 83 + }, + { + "gameId": 687, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 10.5, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 88 + }, + { + "gameId": 688, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 86 + }, + { + "gameId": 689, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 101 + }, + { + "gameId": 690, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 13, + "scoreDiff": 2.5, + "moveCount": 32, + "duration": 134 + }, + { + "gameId": 691, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 17.5, + "scoreDiff": -12.5, + "moveCount": 32, + "duration": 131 + }, + { + "gameId": 692, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 23.5, + "model2Score": 1, + "scoreDiff": -22.5, + "moveCount": 32, + "duration": 134 + }, + { + "gameId": 693, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 99 + }, + { + "gameId": 694, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 28, + "duration": 112 + }, + { + "gameId": 695, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 27, + "duration": 106 + }, + { + "gameId": 696, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 108 + }, + { + "gameId": 697, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 13.5, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 100 + }, + { + "gameId": 698, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 6, + "scoreDiff": -1.5, + "moveCount": 19, + "duration": 70 + }, + { + "gameId": 699, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 4.5, + "scoreDiff": 13.5, + "moveCount": 37, + "duration": 150 + }, + { + "gameId": 700, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 10, + "scoreDiff": 1.5, + "moveCount": 19, + "duration": 67 + }, + { + "gameId": 701, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 90 + }, + { + "gameId": 702, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 8, + "scoreDiff": -1.5, + "moveCount": 20, + "duration": 72 + }, + { + "gameId": 703, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 38, + "duration": 167 + }, + { + "gameId": 704, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 27, + "duration": 148 + }, + { + "gameId": 705, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 9, + "model2Score": 7.5, + "scoreDiff": 1.5, + "moveCount": 19, + "duration": 86 + }, + { + "gameId": 706, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 27, + "duration": 104 + }, + { + "gameId": 707, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 23, + "model2Score": 1.5, + "scoreDiff": 21.5, + "moveCount": 35, + "duration": 147 + }, + { + "gameId": 708, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 11, + "scoreDiff": -2.5, + "moveCount": 28, + "duration": 111 + }, + { + "gameId": 709, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 104 + }, + { + "gameId": 710, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 12, + "scoreDiff": 2.5, + "moveCount": 23, + "duration": 90 + }, + { + "gameId": 711, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 4, + "model2Score": 16.5, + "scoreDiff": -12.5, + "moveCount": 24, + "duration": 116 + }, + { + "gameId": 712, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 14, + "scoreDiff": 5.5, + "moveCount": 27, + "duration": 119 + }, + { + "gameId": 713, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 117 + }, + { + "gameId": 714, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 24, + "duration": 99 + }, + { + "gameId": 715, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 17.5, + "scoreDiff": -12.5, + "moveCount": 38, + "duration": 173 + }, + { + "gameId": 716, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 6.5, + "model2Score": 6, + "scoreDiff": -0.5, + "moveCount": 15, + "duration": 59 + }, + { + "gameId": 717, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 103 + }, + { + "gameId": 718, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 7, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 89 + }, + { + "gameId": 719, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 5.5, + "scoreDiff": 8.5, + "moveCount": 34, + "duration": 158 + }, + { + "gameId": 720, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 2.5, + "model2Score": 22, + "scoreDiff": 19.5, + "moveCount": 33, + "duration": 170 + }, + { + "gameId": 721, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 8, + "model2Score": 6.5, + "scoreDiff": 1.5, + "moveCount": 17, + "duration": 73 + }, + { + "gameId": 722, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 29, + "duration": 121 + }, + { + "gameId": 723, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 9.5, + "scoreDiff": -1.5, + "moveCount": 20, + "duration": 85 + }, + { + "gameId": 724, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 92 + }, + { + "gameId": 725, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 9.5, + "scoreDiff": 6.5, + "moveCount": 29, + "duration": 128 + }, + { + "gameId": 726, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 23, + "duration": 91 + }, + { + "gameId": 727, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 4.5, + "scoreDiff": 11.5, + "moveCount": 41, + "duration": 203 + }, + { + "gameId": 728, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 36, + "duration": 172 + }, + { + "gameId": 729, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 6.5, + "scoreDiff": 11.5, + "moveCount": 31, + "duration": 133 + }, + { + "gameId": 730, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 7, + "scoreDiff": -7.5, + "moveCount": 24, + "duration": 95 + }, + { + "gameId": 731, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 26, + "duration": 117 + }, + { + "gameId": 732, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 12.5, + "model2Score": 13, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 108 + }, + { + "gameId": 733, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 10.5, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 107 + }, + { + "gameId": 734, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 14, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 112 + }, + { + "gameId": 735, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 28, + "duration": 116 + }, + { + "gameId": 736, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 8, + "scoreDiff": -1.5, + "moveCount": 20, + "duration": 78 + }, + { + "gameId": 737, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 45, + "duration": 257 + }, + { + "gameId": 738, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 136 + }, + { + "gameId": 739, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 13.5, + "scoreDiff": -6.5, + "moveCount": 24, + "duration": 97 + }, + { + "gameId": 740, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 28, + "duration": 118 + }, + { + "gameId": 741, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 27, + "duration": 118 + }, + { + "gameId": 742, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 32, + "duration": 138 + }, + { + "gameId": 743, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 101 + }, + { + "gameId": 744, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 7, + "scoreDiff": -1.5, + "moveCount": 19, + "duration": 75 + }, + { + "gameId": 745, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 102 + }, + { + "gameId": 746, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 15, + "scoreDiff": 8.5, + "moveCount": 29, + "duration": 125 + }, + { + "gameId": 747, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 107 + }, + { + "gameId": 748, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 8, + "scoreDiff": -5.5, + "moveCount": 28, + "duration": 120 + }, + { + "gameId": 749, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 13.5, + "scoreDiff": -1.5, + "moveCount": 32, + "duration": 138 + }, + { + "gameId": 750, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 8, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 88 + }, + { + "gameId": 751, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 13.5, + "scoreDiff": -5.5, + "moveCount": 26, + "duration": 110 + }, + { + "gameId": 752, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 18, + "scoreDiff": 11.5, + "moveCount": 31, + "duration": 138 + }, + { + "gameId": 753, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 134 + }, + { + "gameId": 754, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 8, + "scoreDiff": -0.5, + "moveCount": 19, + "duration": 63 + }, + { + "gameId": 755, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 88 + }, + { + "gameId": 756, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 88 + }, + { + "gameId": 757, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 107 + }, + { + "gameId": 758, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 12, + "scoreDiff": 2.5, + "moveCount": 25, + "duration": 99 + }, + { + "gameId": 759, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 15.5, + "scoreDiff": -8.5, + "moveCount": 22, + "duration": 88 + }, + { + "gameId": 760, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 23.5, + "model2Score": 1, + "scoreDiff": -22.5, + "moveCount": 38, + "duration": 179 + }, + { + "gameId": 761, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 27, + "duration": 112 + }, + { + "gameId": 762, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 11, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 105 + }, + { + "gameId": 763, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 12.5, + "scoreDiff": 0.5, + "moveCount": 33, + "duration": 156 + }, + { + "gameId": 764, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 9, + "scoreDiff": -3.5, + "moveCount": 23, + "duration": 91 + }, + { + "gameId": 765, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 23, + "duration": 95 + }, + { + "gameId": 766, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 7, + "scoreDiff": -1.5, + "moveCount": 19, + "duration": 73 + }, + { + "gameId": 767, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 28, + "duration": 118 + }, + { + "gameId": 768, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 30, + "duration": 131 + }, + { + "gameId": 769, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 14.5, + "scoreDiff": -6.5, + "moveCount": 24, + "duration": 94 + }, + { + "gameId": 770, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 11, + "scoreDiff": -2.5, + "moveCount": 28, + "duration": 115 + }, + { + "gameId": 771, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 15.5, + "scoreDiff": -9.5, + "moveCount": 28, + "duration": 137 + }, + { + "gameId": 772, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 5, + "scoreDiff": -11.5, + "moveCount": 29, + "duration": 132 + }, + { + "gameId": 773, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 7.5, + "scoreDiff": 8.5, + "moveCount": 35, + "duration": 151 + }, + { + "gameId": 774, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 25, + "duration": 97 + }, + { + "gameId": 775, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 7.5, + "scoreDiff": 7.5, + "moveCount": 29, + "duration": 115 + }, + { + "gameId": 776, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 6.5, + "model2Score": 4, + "scoreDiff": -2.5, + "moveCount": 13, + "duration": 44 + }, + { + "gameId": 777, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 9.5, + "scoreDiff": 6.5, + "moveCount": 33, + "duration": 143 + }, + { + "gameId": 778, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 7, + "scoreDiff": -6.5, + "moveCount": 26, + "duration": 133 + }, + { + "gameId": 779, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 5.5, + "scoreDiff": 10.5, + "moveCount": 33, + "duration": 173 + }, + { + "gameId": 780, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 22, + "duration": 89 + }, + { + "gameId": 781, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 31, + "duration": 139 + }, + { + "gameId": 782, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 125 + }, + { + "gameId": 783, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 21, + "model2Score": 2.5, + "scoreDiff": 18.5, + "moveCount": 31, + "duration": 142 + }, + { + "gameId": 784, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 4, + "scoreDiff": -13.5, + "moveCount": 30, + "duration": 144 + }, + { + "gameId": 785, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 16.5, + "scoreDiff": -10.5, + "moveCount": 32, + "duration": 123 + }, + { + "gameId": 786, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 131 + }, + { + "gameId": 787, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 111 + }, + { + "gameId": 788, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 108 + }, + { + "gameId": 789, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 10.5, + "scoreDiff": -1.5, + "moveCount": 21, + "duration": 87 + }, + { + "gameId": 790, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 7, + "scoreDiff": -5.5, + "moveCount": 27, + "duration": 108 + }, + { + "gameId": 791, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 24, + "duration": 98 + }, + { + "gameId": 792, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 11, + "scoreDiff": 2.5, + "moveCount": 23, + "duration": 88 + }, + { + "gameId": 793, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 9.5, + "scoreDiff": 3.5, + "moveCount": 28, + "duration": 118 + }, + { + "gameId": 794, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 14, + "scoreDiff": 3.5, + "moveCount": 31, + "duration": 134 + }, + { + "gameId": 795, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 3.5, + "scoreDiff": 14.5, + "moveCount": 35, + "duration": 163 + }, + { + "gameId": 796, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 11, + "scoreDiff": -2.5, + "moveCount": 30, + "duration": 162 + }, + { + "gameId": 797, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 11.5, + "scoreDiff": -4.5, + "moveCount": 24, + "duration": 97 + }, + { + "gameId": 798, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 20.5, + "model2Score": 1, + "scoreDiff": -19.5, + "moveCount": 38, + "duration": 180 + }, + { + "gameId": 799, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 12.5, + "scoreDiff": -3.5, + "moveCount": 24, + "duration": 100 + }, + { + "gameId": 800, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 30, + "duration": 129 + }, + { + "gameId": 801, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 4.5, + "scoreDiff": 11.5, + "moveCount": 31, + "duration": 133 + }, + { + "gameId": 802, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 15, + "scoreDiff": 7.5, + "moveCount": 23, + "duration": 94 + }, + { + "gameId": 803, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 8.5, + "scoreDiff": 5.5, + "moveCount": 29, + "duration": 122 + }, + { + "gameId": 804, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 12.5, + "model2Score": 13, + "scoreDiff": 0.5, + "moveCount": 29, + "duration": 123 + }, + { + "gameId": 805, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 15.5, + "scoreDiff": -6.5, + "moveCount": 30, + "duration": 134 + }, + { + "gameId": 806, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 27, + "duration": 110 + }, + { + "gameId": 807, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 29, + "duration": 124 + }, + { + "gameId": 808, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 11, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 104 + }, + { + "gameId": 809, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 12.5, + "scoreDiff": -2.5, + "moveCount": 25, + "duration": 116 + }, + { + "gameId": 810, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 26, + "duration": 111 + }, + { + "gameId": 811, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 11.5, + "scoreDiff": -2.5, + "moveCount": 29, + "duration": 126 + }, + { + "gameId": 812, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 5, + "scoreDiff": -9.5, + "moveCount": 28, + "duration": 117 + }, + { + "gameId": 813, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 11.5, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 102 + }, + { + "gameId": 814, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 87 + }, + { + "gameId": 815, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 24, + "duration": 90 + }, + { + "gameId": 816, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 9, + "scoreDiff": -2.5, + "moveCount": 24, + "duration": 98 + }, + { + "gameId": 817, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 26, + "duration": 103 + }, + { + "gameId": 818, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 5, + "scoreDiff": -11.5, + "moveCount": 30, + "duration": 132 + }, + { + "gameId": 819, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 11.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 104 + }, + { + "gameId": 820, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 10, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 91 + }, + { + "gameId": 821, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 13.5, + "scoreDiff": -6.5, + "moveCount": 27, + "duration": 118 + }, + { + "gameId": 822, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 14, + "scoreDiff": 2.5, + "moveCount": 23, + "duration": 85 + }, + { + "gameId": 823, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 15.5, + "scoreDiff": -8.5, + "moveCount": 29, + "duration": 119 + }, + { + "gameId": 824, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 27, + "duration": 112 + }, + { + "gameId": 825, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 125 + }, + { + "gameId": 826, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 17, + "scoreDiff": 10.5, + "moveCount": 27, + "duration": 109 + }, + { + "gameId": 827, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 18.5, + "scoreDiff": -12.5, + "moveCount": 34, + "duration": 180 + }, + { + "gameId": 828, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 21, + "duration": 81 + }, + { + "gameId": 829, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 29, + "duration": 126 + }, + { + "gameId": 830, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 36, + "duration": 168 + }, + { + "gameId": 831, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 14.5, + "scoreDiff": -7.5, + "moveCount": 29, + "duration": 123 + }, + { + "gameId": 832, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 9, + "scoreDiff": -5.5, + "moveCount": 26, + "duration": 108 + }, + { + "gameId": 833, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 21, + "model2Score": 3.5, + "scoreDiff": 17.5, + "moveCount": 35, + "duration": 154 + }, + { + "gameId": 834, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 6, + "scoreDiff": -10.5, + "moveCount": 30, + "duration": 126 + }, + { + "gameId": 835, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 34, + "duration": 151 + }, + { + "gameId": 836, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 15, + "scoreDiff": 8.5, + "moveCount": 40, + "duration": 189 + }, + { + "gameId": 837, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 132 + }, + { + "gameId": 838, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 8, + "scoreDiff": -5.5, + "moveCount": 26, + "duration": 110 + }, + { + "gameId": 839, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 3.5, + "scoreDiff": 14.5, + "moveCount": 27, + "duration": 113 + }, + { + "gameId": 840, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 31, + "duration": 136 + }, + { + "gameId": 841, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 6.5, + "scoreDiff": 9.5, + "moveCount": 36, + "duration": 161 + }, + { + "gameId": 842, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 113 + }, + { + "gameId": 843, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 11.5, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 106 + }, + { + "gameId": 844, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 6, + "scoreDiff": -10.5, + "moveCount": 36, + "duration": 161 + }, + { + "gameId": 845, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 25, + "duration": 102 + }, + { + "gameId": 846, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 10.5, + "model2Score": 15, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 115 + }, + { + "gameId": 847, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 23, + "model2Score": 1.5, + "scoreDiff": 21.5, + "moveCount": 39, + "duration": 202 + }, + { + "gameId": 848, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 7, + "scoreDiff": 0.5, + "moveCount": 15, + "duration": 58 + }, + { + "gameId": 849, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 24, + "duration": 93 + }, + { + "gameId": 850, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 17.5, + "model2Score": 5, + "scoreDiff": -12.5, + "moveCount": 31, + "duration": 135 + }, + { + "gameId": 851, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 9.5, + "scoreDiff": 2.5, + "moveCount": 24, + "duration": 96 + }, + { + "gameId": 852, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 9, + "scoreDiff": -5.5, + "moveCount": 30, + "duration": 130 + }, + { + "gameId": 853, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 26, + "duration": 101 + }, + { + "gameId": 854, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 10, + "scoreDiff": -4.5, + "moveCount": 32, + "duration": 139 + }, + { + "gameId": 855, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 102 + }, + { + "gameId": 856, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 8, + "scoreDiff": -5.5, + "moveCount": 28, + "duration": 126 + }, + { + "gameId": 857, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 14.5, + "scoreDiff": -7.5, + "moveCount": 26, + "duration": 104 + }, + { + "gameId": 858, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 5, + "scoreDiff": -8.5, + "moveCount": 26, + "duration": 112 + }, + { + "gameId": 859, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 13.5, + "scoreDiff": -4.5, + "moveCount": 35, + "duration": 157 + }, + { + "gameId": 860, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 7, + "scoreDiff": -3.5, + "moveCount": 20, + "duration": 78 + }, + { + "gameId": 861, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 5, + "model2Score": 5.5, + "scoreDiff": -0.5, + "moveCount": 12, + "duration": 37 + }, + { + "gameId": 862, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 20.5, + "model2Score": 3, + "scoreDiff": -17.5, + "moveCount": 28, + "duration": 120 + }, + { + "gameId": 863, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 6, + "model2Score": 7.5, + "scoreDiff": -1.5, + "moveCount": 16, + "duration": 63 + }, + { + "gameId": 864, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 6.5, + "model2Score": 16, + "scoreDiff": 9.5, + "moveCount": 27, + "duration": 109 + }, + { + "gameId": 865, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 11.5, + "scoreDiff": -0.5, + "moveCount": 26, + "duration": 103 + }, + { + "gameId": 866, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 23, + "duration": 92 + }, + { + "gameId": 867, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 20, + "model2Score": 4.5, + "scoreDiff": 15.5, + "moveCount": 31, + "duration": 130 + }, + { + "gameId": 868, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 13, + "scoreDiff": 1.5, + "moveCount": 29, + "duration": 119 + }, + { + "gameId": 869, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 9.5, + "scoreDiff": 1.5, + "moveCount": 22, + "duration": 88 + }, + { + "gameId": 870, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 104 + }, + { + "gameId": 871, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 6.5, + "scoreDiff": 11.5, + "moveCount": 29, + "duration": 125 + }, + { + "gameId": 872, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 7, + "scoreDiff": -3.5, + "moveCount": 24, + "duration": 97 + }, + { + "gameId": 873, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 26, + "duration": 100 + }, + { + "gameId": 874, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 10, + "scoreDiff": -0.5, + "moveCount": 23, + "duration": 88 + }, + { + "gameId": 875, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 15.5, + "scoreDiff": -7.5, + "moveCount": 26, + "duration": 91 + }, + { + "gameId": 876, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 11, + "scoreDiff": -1.5, + "moveCount": 28, + "duration": 118 + }, + { + "gameId": 877, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 6.5, + "scoreDiff": 10.5, + "moveCount": 31, + "duration": 132 + }, + { + "gameId": 878, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 7, + "scoreDiff": -2.5, + "moveCount": 19, + "duration": 75 + }, + { + "gameId": 879, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 23, + "duration": 89 + }, + { + "gameId": 880, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 10, + "scoreDiff": -1.5, + "moveCount": 25, + "duration": 102 + }, + { + "gameId": 881, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 27, + "duration": 110 + }, + { + "gameId": 882, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 23.5, + "model2Score": 1, + "scoreDiff": -22.5, + "moveCount": 32, + "duration": 157 + }, + { + "gameId": 883, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 114 + }, + { + "gameId": 884, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 8, + "scoreDiff": -5.5, + "moveCount": 27, + "duration": 111 + }, + { + "gameId": 885, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 2.5, + "scoreDiff": 16.5, + "moveCount": 32, + "duration": 142 + }, + { + "gameId": 886, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 5, + "scoreDiff": -8.5, + "moveCount": 24, + "duration": 102 + }, + { + "gameId": 887, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 11.5, + "scoreDiff": -4.5, + "moveCount": 24, + "duration": 99 + }, + { + "gameId": 888, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 32, + "duration": 147 + }, + { + "gameId": 889, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 9.5, + "scoreDiff": 4.5, + "moveCount": 25, + "duration": 98 + }, + { + "gameId": 890, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 18, + "duration": 65 + }, + { + "gameId": 891, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 22, + "model2Score": 1.5, + "scoreDiff": 20.5, + "moveCount": 27, + "duration": 112 + }, + { + "gameId": 892, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 4.5, + "model2Score": 18, + "scoreDiff": 13.5, + "moveCount": 39, + "duration": 190 + }, + { + "gameId": 893, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 3, + "duration": 12 + }, + { + "gameId": 894, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 22.5, + "model2Score": 1, + "scoreDiff": -21.5, + "moveCount": 36, + "duration": 175 + }, + { + "gameId": 895, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 6, + "model2Score": 5.5, + "scoreDiff": 0.5, + "moveCount": 12, + "duration": 37 + }, + { + "gameId": 896, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 25, + "duration": 98 + }, + { + "gameId": 897, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 6.5, + "scoreDiff": 11.5, + "moveCount": 27, + "duration": 115 + }, + { + "gameId": 898, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 22, + "duration": 88 + }, + { + "gameId": 899, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 12.5, + "scoreDiff": 0.5, + "moveCount": 27, + "duration": 115 + }, + { + "gameId": 900, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 8, + "scoreDiff": -5.5, + "moveCount": 26, + "duration": 104 + }, + { + "gameId": 901, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 35, + "duration": 164 + }, + { + "gameId": 902, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 6, + "scoreDiff": -9.5, + "moveCount": 23, + "duration": 92 + }, + { + "gameId": 903, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 128 + }, + { + "gameId": 904, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 15, + "scoreDiff": 6.5, + "moveCount": 33, + "duration": 143 + }, + { + "gameId": 905, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 17, + "model2Score": 5.5, + "scoreDiff": 11.5, + "moveCount": 33, + "duration": 153 + }, + { + "gameId": 906, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 12.5, + "model2Score": 13, + "scoreDiff": 0.5, + "moveCount": 34, + "duration": 188 + }, + { + "gameId": 907, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 13.5, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 129 + }, + { + "gameId": 908, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 10, + "scoreDiff": 1.5, + "moveCount": 19, + "duration": 77 + }, + { + "gameId": 909, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 3, + "model2Score": 12.5, + "scoreDiff": -9.5, + "moveCount": 33, + "duration": 156 + }, + { + "gameId": 910, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 6, + "scoreDiff": -13.5, + "moveCount": 30, + "duration": 131 + }, + { + "gameId": 911, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 14, + "model2Score": 10.5, + "scoreDiff": 3.5, + "moveCount": 25, + "duration": 101 + }, + { + "gameId": 912, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 11, + "scoreDiff": 2.5, + "moveCount": 21, + "duration": 84 + }, + { + "gameId": 913, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 5.5, + "scoreDiff": 13.5, + "moveCount": 33, + "duration": 144 + }, + { + "gameId": 914, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 6, + "scoreDiff": -10.5, + "moveCount": 34, + "duration": 150 + }, + { + "gameId": 915, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 12.5, + "scoreDiff": -1.5, + "moveCount": 32, + "duration": 145 + }, + { + "gameId": 916, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 7, + "scoreDiff": -2.5, + "moveCount": 18, + "duration": 64 + }, + { + "gameId": 917, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 14.5, + "scoreDiff": -4.5, + "moveCount": 30, + "duration": 119 + }, + { + "gameId": 918, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 5, + "scoreDiff": -14.5, + "moveCount": 32, + "duration": 147 + }, + { + "gameId": 919, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 28, + "duration": 111 + }, + { + "gameId": 920, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 5, + "scoreDiff": -13.5, + "moveCount": 32, + "duration": 148 + }, + { + "gameId": 921, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 5.5, + "scoreDiff": 12.5, + "moveCount": 31, + "duration": 139 + }, + { + "gameId": 922, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 11, + "scoreDiff": 3.5, + "moveCount": 23, + "duration": 88 + }, + { + "gameId": 923, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 4, + "model2Score": 5.5, + "scoreDiff": -1.5, + "moveCount": 12, + "duration": 40 + }, + { + "gameId": 924, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 10, + "scoreDiff": -5.5, + "moveCount": 36, + "duration": 178 + }, + { + "gameId": 925, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 113 + }, + { + "gameId": 926, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 10, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 114 + }, + { + "gameId": 927, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 9, + "model2Score": 8.5, + "scoreDiff": 0.5, + "moveCount": 22, + "duration": 93 + }, + { + "gameId": 928, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 11, + "scoreDiff": -2.5, + "moveCount": 30, + "duration": 122 + }, + { + "gameId": 929, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 8.5, + "scoreDiff": 6.5, + "moveCount": 26, + "duration": 109 + }, + { + "gameId": 930, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 7, + "scoreDiff": -7.5, + "moveCount": 28, + "duration": 124 + }, + { + "gameId": 931, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 30, + "duration": 130 + }, + { + "gameId": 932, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 25, + "duration": 106 + }, + { + "gameId": 933, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 11.5, + "scoreDiff": -1.5, + "moveCount": 22, + "duration": 85 + }, + { + "gameId": 934, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 34, + "duration": 161 + }, + { + "gameId": 935, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 10.5, + "scoreDiff": -2.5, + "moveCount": 22, + "duration": 90 + }, + { + "gameId": 936, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 7, + "scoreDiff": -8.5, + "moveCount": 28, + "duration": 118 + }, + { + "gameId": 937, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 23, + "duration": 94 + }, + { + "gameId": 938, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 8.5, + "model2Score": 7, + "scoreDiff": -1.5, + "moveCount": 19, + "duration": 71 + }, + { + "gameId": 939, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 18, + "model2Score": 2.5, + "scoreDiff": 15.5, + "moveCount": 34, + "duration": 152 + }, + { + "gameId": 940, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 12.5, + "model2Score": 13, + "scoreDiff": 0.5, + "moveCount": 28, + "duration": 113 + }, + { + "gameId": 941, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 8, + "model2Score": 15.5, + "scoreDiff": -7.5, + "moveCount": 30, + "duration": 134 + }, + { + "gameId": 942, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 32, + "duration": 147 + }, + { + "gameId": 943, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 27, + "duration": 119 + }, + { + "gameId": 944, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 4, + "scoreDiff": -11.5, + "moveCount": 28, + "duration": 155 + }, + { + "gameId": 945, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 11, + "model2Score": 13.5, + "scoreDiff": -2.5, + "moveCount": 26, + "duration": 106 + }, + { + "gameId": 946, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 8.5, + "model2Score": 14, + "scoreDiff": 5.5, + "moveCount": 29, + "duration": 117 + }, + { + "gameId": 947, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 23, + "model2Score": 1.5, + "scoreDiff": 21.5, + "moveCount": 41, + "duration": 208 + }, + { + "gameId": 948, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 21, + "duration": 83 + }, + { + "gameId": 949, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 26, + "duration": 103 + }, + { + "gameId": 950, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 5, + "scoreDiff": -11.5, + "moveCount": 26, + "duration": 107 + }, + { + "gameId": 951, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 15, + "model2Score": 9.5, + "scoreDiff": 5.5, + "moveCount": 31, + "duration": 140 + }, + { + "gameId": 952, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 11.5, + "model2Score": 12, + "scoreDiff": 0.5, + "moveCount": 33, + "duration": 148 + }, + { + "gameId": 953, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 26, + "duration": 103 + }, + { + "gameId": 954, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 9.5, + "model2Score": 9, + "scoreDiff": -0.5, + "moveCount": 21, + "duration": 83 + }, + { + "gameId": 955, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 112 + }, + { + "gameId": 956, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 16, + "scoreDiff": 6.5, + "moveCount": 27, + "duration": 113 + }, + { + "gameId": 957, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 8.5, + "scoreDiff": 4.5, + "moveCount": 24, + "duration": 99 + }, + { + "gameId": 958, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 9, + "scoreDiff": -5.5, + "moveCount": 38, + "duration": 179 + }, + { + "gameId": 959, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 24, + "duration": 114 + }, + { + "gameId": 960, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 33, + "duration": 167 + }, + { + "gameId": 961, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 10.5, + "scoreDiff": -1.5, + "moveCount": 22, + "duration": 87 + }, + { + "gameId": 962, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 9, + "scoreDiff": -4.5, + "moveCount": 28, + "duration": 121 + }, + { + "gameId": 963, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 7, + "model2Score": 14.5, + "scoreDiff": -7.5, + "moveCount": 30, + "duration": 130 + }, + { + "gameId": 964, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 7.5, + "model2Score": 7, + "scoreDiff": -0.5, + "moveCount": 17, + "duration": 64 + }, + { + "gameId": 965, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 11.5, + "scoreDiff": 1.5, + "moveCount": 33, + "duration": 142 + }, + { + "gameId": 966, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 25.5, + "model2Score": 0, + "scoreDiff": -25.5, + "moveCount": 30, + "duration": 155 + }, + { + "gameId": 967, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 24, + "duration": 102 + }, + { + "gameId": 968, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 16.5, + "model2Score": 7, + "scoreDiff": -9.5, + "moveCount": 30, + "duration": 135 + }, + { + "gameId": 969, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 16, + "model2Score": 4.5, + "scoreDiff": 11.5, + "moveCount": 41, + "duration": 201 + }, + { + "gameId": 970, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 12.5, + "model2Score": 13, + "scoreDiff": 0.5, + "moveCount": 30, + "duration": 130 + }, + { + "gameId": 971, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 28, + "duration": 123 + }, + { + "gameId": 972, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 15.5, + "model2Score": 8, + "scoreDiff": -7.5, + "moveCount": 28, + "duration": 129 + }, + { + "gameId": 973, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 25, + "model2Score": 0.5, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 109 + }, + { + "gameId": 974, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 7.5, + "model2Score": 11, + "scoreDiff": 3.5, + "moveCount": 21, + "duration": 95 + }, + { + "gameId": 975, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 0, + "model2Score": 25.5, + "scoreDiff": -25.5, + "moveCount": 40, + "duration": 198 + }, + { + "gameId": 976, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 19.5, + "model2Score": 4, + "scoreDiff": -15.5, + "moveCount": 28, + "duration": 117 + }, + { + "gameId": 977, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 13.5, + "scoreDiff": -3.5, + "moveCount": 25, + "duration": 103 + }, + { + "gameId": 978, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 29, + "duration": 123 + }, + { + "gameId": 979, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 9, + "model2Score": 10.5, + "scoreDiff": -1.5, + "moveCount": 24, + "duration": 96 + }, + { + "gameId": 980, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 9.5, + "model2Score": 11, + "scoreDiff": 1.5, + "moveCount": 21, + "duration": 81 + }, + { + "gameId": 981, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 9.5, + "scoreDiff": 1.5, + "moveCount": 24, + "duration": 94 + }, + { + "gameId": 982, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 22.5, + "model2Score": 1, + "scoreDiff": -21.5, + "moveCount": 50, + "duration": 282 + }, + { + "gameId": 983, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 11.5, + "scoreDiff": -1.5, + "moveCount": 24, + "duration": 93 + }, + { + "gameId": 984, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 3.5, + "model2Score": 4, + "scoreDiff": 0.5, + "moveCount": 9, + "duration": 32 + }, + { + "gameId": 985, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 12, + "model2Score": 12.5, + "scoreDiff": -0.5, + "moveCount": 25, + "duration": 99 + }, + { + "gameId": 986, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 5.5, + "model2Score": 18, + "scoreDiff": 12.5, + "moveCount": 27, + "duration": 108 + }, + { + "gameId": 987, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 8.5, + "scoreDiff": 4.5, + "moveCount": 27, + "duration": 118 + }, + { + "gameId": 988, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 18.5, + "model2Score": 4, + "scoreDiff": -14.5, + "moveCount": 34, + "duration": 156 + }, + { + "gameId": 989, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 13, + "model2Score": 10.5, + "scoreDiff": 2.5, + "moveCount": 25, + "duration": 99 + }, + { + "gameId": 990, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 10.5, + "model2Score": 7, + "scoreDiff": -3.5, + "moveCount": 22, + "duration": 89 + }, + { + "gameId": 991, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 11, + "model2Score": 10.5, + "scoreDiff": 0.5, + "moveCount": 22, + "duration": 86 + }, + { + "gameId": 992, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 11.5, + "model2Score": 6, + "scoreDiff": -5.5, + "moveCount": 24, + "duration": 97 + }, + { + "gameId": 993, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 10, + "model2Score": 11.5, + "scoreDiff": -1.5, + "moveCount": 26, + "duration": 104 + }, + { + "gameId": 994, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 25, + "scoreDiff": 24.5, + "moveCount": 27, + "duration": 114 + }, + { + "gameId": 995, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 11.5, + "scoreDiff": 0.5, + "moveCount": 30, + "duration": 130 + }, + { + "gameId": 996, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 14.5, + "model2Score": 8, + "scoreDiff": -6.5, + "moveCount": 29, + "duration": 122 + }, + { + "gameId": 997, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 12, + "model2Score": 10.5, + "scoreDiff": 1.5, + "moveCount": 27, + "duration": 110 + }, + { + "gameId": 998, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 12.5, + "model2Score": 12, + "scoreDiff": -0.5, + "moveCount": 28, + "duration": 115 + }, + { + "gameId": 999, + "model1AsBlack": true, + "winner": "model1", + "model1Score": 19, + "model2Score": 6.5, + "scoreDiff": 12.5, + "moveCount": 27, + "duration": 117 + }, + { + "gameId": 1000, + "model1AsBlack": false, + "winner": "model1", + "model1Score": 13.5, + "model2Score": 10, + "scoreDiff": -3.5, + "moveCount": 31, + "duration": 139 + } + ] + }, + "timestamp": "2025-12-28T01:34:06.758Z" +} \ No newline at end of file diff --git a/trigo-web/tools/output/battle_test/battle_2025-12-17T07-37-16-800Z.json b/trigo-web/tools/output/battle_test/battle_2025-12-17T07-37-16-800Z.json new file mode 100644 index 0000000000000000000000000000000000000000..7a0e2afdcde6acfe91670d04b0ba8d8065252915 --- /dev/null +++ b/trigo-web/tools/output/battle_test/battle_2025-12-17T07-37-16-800Z.json @@ -0,0 +1,76 @@ +{ + "config": { + "model1Tree": "./public/onnx/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_tree.onnx", + "model2Tree": "./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_tree.onnx", + "model1Eval": "./public/onnx/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_evaluation.onnx", + "model2Eval": "./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_evaluation.onnx", + "boardShape": { + "x": 5, + "y": 5, + "z": 5 + }, + "numGames": 4, + "temperature": 1, + "useMCTS": false, + "mctsSimulations": 600, + "komi": 0.5 + }, + "statistics": { + "totalGames": 4, + "model1Wins": 0, + "model2Wins": 4, + "draws": 0, + "model1WinRate": 0, + "model2WinRate": 100, + "model1AsBlackWins": 0, + "model1AsWhiteWins": 0, + "model2AsBlackWins": 2, + "model2AsWhiteWins": 2, + "averageGameLength": 130, + "averageScoreDiff": 33.75, + "totalTime": 9650, + "results": [ + { + "gameId": 1, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 18, + "model2Score": 23.5, + "scoreDiff": -5.5, + "moveCount": 48, + "duration": 457 + }, + { + "gameId": 2, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 125, + "scoreDiff": 124.5, + "moveCount": 145, + "duration": 2504 + }, + { + "gameId": 3, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 38, + "model2Score": 75.5, + "scoreDiff": -37.5, + "moveCount": 150, + "duration": 2792 + }, + { + "gameId": 4, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 35.5, + "model2Score": 89, + "scoreDiff": 53.5, + "moveCount": 177, + "duration": 3894 + } + ] + }, + "timestamp": "2025-12-17T07:37:16.800Z" +} \ No newline at end of file diff --git a/trigo-web/tools/output/battle_test2/battle_2025-12-17T07-40-15-805Z.json b/trigo-web/tools/output/battle_test2/battle_2025-12-17T07-40-15-805Z.json new file mode 100644 index 0000000000000000000000000000000000000000..42ebd6ba520b728d0485be777cf82d9d3909f536 --- /dev/null +++ b/trigo-web/tools/output/battle_test2/battle_2025-12-17T07-40-15-805Z.json @@ -0,0 +1,56 @@ +{ + "config": { + "model1Tree": "./public/onnx/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_tree.onnx", + "model2Tree": "./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_tree.onnx", + "model1Eval": "./public/onnx/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_evaluation.onnx", + "model2Eval": "./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_evaluation.onnx", + "boardShape": { + "x": 5, + "y": 5, + "z": 5 + }, + "numGames": 2, + "temperature": 1, + "useMCTS": false, + "mctsSimulations": 600, + "komi": 0.5 + }, + "statistics": { + "totalGames": 2, + "model1Wins": 0, + "model2Wins": 2, + "draws": 0, + "model1WinRate": 0, + "model2WinRate": 100, + "model1AsBlackWins": 0, + "model1AsWhiteWins": 0, + "model2AsBlackWins": 1, + "model2AsWhiteWins": 1, + "averageGameLength": 137, + "averageScoreDiff": -15, + "totalTime": 4714, + "results": [ + { + "gameId": 1, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 26, + "model2Score": 68.5, + "scoreDiff": -42.5, + "moveCount": 138, + "duration": 2653 + }, + { + "gameId": 2, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 54.5, + "model2Score": 67, + "scoreDiff": 12.5, + "moveCount": 136, + "duration": 2059 + } + ] + }, + "timestamp": "2025-12-17T07:40:15.806Z" +} \ No newline at end of file diff --git a/trigo-web/tools/output/battle_test3/battle_2025-12-17T07-41-45-839Z.json b/trigo-web/tools/output/battle_test3/battle_2025-12-17T07-41-45-839Z.json new file mode 100644 index 0000000000000000000000000000000000000000..dc279cdaf6942ecfa32f8901a62c5d240d5d8253 --- /dev/null +++ b/trigo-web/tools/output/battle_test3/battle_2025-12-17T07-41-45-839Z.json @@ -0,0 +1,56 @@ +{ + "config": { + "model1Tree": "./public/onnx/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_tree.onnx", + "model2Tree": "./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_tree.onnx", + "model1Eval": "./public/onnx/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_evaluation.onnx", + "model2Eval": "./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_evaluation.onnx", + "boardShape": { + "x": 5, + "y": 5, + "z": 5 + }, + "numGames": 2, + "temperature": 1, + "useMCTS": false, + "mctsSimulations": 600, + "komi": 0.5 + }, + "statistics": { + "totalGames": 2, + "model1Wins": 0, + "model2Wins": 2, + "draws": 0, + "model1WinRate": 0, + "model2WinRate": 100, + "model1AsBlackWins": 0, + "model1AsWhiteWins": 0, + "model2AsBlackWins": 1, + "model2AsWhiteWins": 1, + "averageGameLength": 119.5, + "averageScoreDiff": 49, + "totalTime": 3806, + "results": [ + { + "gameId": 1, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 19, + "model2Score": 45.5, + "scoreDiff": -26.5, + "moveCount": 88, + "duration": 1027 + }, + { + "gameId": 2, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 0.5, + "model2Score": 125, + "scoreDiff": 124.5, + "moveCount": 151, + "duration": 2776 + } + ] + }, + "timestamp": "2025-12-17T07:41:45.840Z" +} \ No newline at end of file diff --git a/trigo-web/tools/output/battle_test4/battle_2025-12-17T08-07-44-986Z.json b/trigo-web/tools/output/battle_test4/battle_2025-12-17T08-07-44-986Z.json new file mode 100644 index 0000000000000000000000000000000000000000..f410f7437edecf9b2095428d8d6517f606a4c76a --- /dev/null +++ b/trigo-web/tools/output/battle_test4/battle_2025-12-17T08-07-44-986Z.json @@ -0,0 +1,56 @@ +{ + "config": { + "model1Tree": "./public/onnx/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_tree.onnx", + "model2Tree": "./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_tree.onnx", + "model1Eval": "./public/onnx/20251215-trigo-value-llama-l6-h64-251211/LlamaCausalLM_ep0045_evaluation.onnx", + "model2Eval": "./public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_evaluation.onnx", + "boardShape": { + "x": 5, + "y": 5, + "z": 5 + }, + "numGames": 2, + "temperature": 1, + "useMCTS": false, + "mctsSimulations": 600, + "komi": 0.5 + }, + "statistics": { + "totalGames": 2, + "model1Wins": 0, + "model2Wins": 2, + "draws": 0, + "model1WinRate": 0, + "model2WinRate": 100, + "model1AsBlackWins": 0, + "model1AsWhiteWins": 0, + "model2AsBlackWins": 1, + "model2AsWhiteWins": 1, + "averageGameLength": 142, + "averageScoreDiff": -23.5, + "totalTime": 5101, + "results": [ + { + "gameId": 1, + "model1AsBlack": true, + "winner": "model2", + "model1Score": 21, + "model2Score": 73.5, + "scoreDiff": -52.5, + "moveCount": 142, + "duration": 2818 + }, + { + "gameId": 2, + "model1AsBlack": false, + "winner": "model2", + "model1Score": 59.5, + "model2Score": 65, + "scoreDiff": 5.5, + "moveCount": 142, + "duration": 2281 + } + ] + }, + "timestamp": "2025-12-17T08:07:50.088Z" +} \ No newline at end of file diff --git a/trigo-web/tools/output/debug_selfplay/game_1_visit_counts.json b/trigo-web/tools/output/debug_selfplay/game_1_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..c7b9c003b13b7a5a37ecda21bc4fb17e2d20b8f7 --- /dev/null +++ b/trigo-web/tools/output/debug_selfplay/game_1_visit_counts.json @@ -0,0 +1,61 @@ +[ + [ + 7, + 25, + 19, + 15, + 18, + 8, + 7, + 0 + ], + [ + 21, + 13, + 11, + 29, + 11, + 14, + 0 + ], + [ + 5, + 12, + 74, + 4, + 4, + 0 + ], + [ + 9, + 2, + 3, + 77, + 8 + ], + [ + 6, + 6, + 87, + 0 + ], + [ + 37, + 4, + 58 + ], + [ + 64, + 27, + 8, + 0 + ], + [ + 99 + ], + [ + 82, + 17, + 0 + ] +] \ No newline at end of file diff --git a/trigo-web/tools/output/selfplay-mcts-5x5-v2/game_1_visit_counts.json b/trigo-web/tools/output/selfplay-mcts-5x5-v2/game_1_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..65d4dd800bda9eee09e727dd3aefcfdfa2d8ddc3 --- /dev/null +++ b/trigo-web/tools/output/selfplay-mcts-5x5-v2/game_1_visit_counts.json @@ -0,0 +1,434 @@ +[ + [ + 7, + 42, + 4, + 8, + 10, + 4, + 4, + 3, + 4, + 4, + 3, + 4, + 4, + 5, + 4, + 5, + 6, + 4, + 11, + 6, + 12, + 12, + 7, + 12, + 13, + 1 + ], + [ + 16, + 4, + 6, + 4, + 61, + 5, + 0, + 0, + 0, + 4, + 9, + 2, + 4, + 2, + 9, + 0, + 13, + 0, + 4, + 17, + 5, + 6, + 4, + 15, + 9 + ], + [ + 12, + 5, + 6, + 5, + 17, + 6, + 3, + 3, + 3, + 10, + 11, + 42, + 8, + 5, + 12, + 3, + 3, + 3, + 7, + 15, + 6, + 7, + 6, + 1 + ], + [ + 19, + 6, + 17, + 6, + 15, + 10, + 0, + 0, + 0, + 7, + 12, + 0, + 0, + 9, + 0, + 0, + 0, + 7, + 70, + 7, + 7, + 7, + 0 + ], + [ + 15, + 9, + 37, + 10, + 12, + 5, + 4, + 2, + 4, + 4, + 7, + 26, + 7, + 4, + 2, + 4, + 5, + 12, + 11, + 7, + 11, + 1 + ], + [ + 18, + 31, + 7, + 7, + 15, + 10, + 0, + 0, + 0, + 7, + 13, + 6, + 5, + 0, + 0, + 0, + 8, + 23, + 33, + 16, + 0 + ], + [ + 43, + 8, + 6, + 8, + 11, + 6, + 4, + 2, + 4, + 5, + 15, + 10, + 5, + 3, + 30, + 5, + 14, + 10, + 9, + 1 + ], + [ + 12, + 37, + 12, + 21, + 8, + 5, + 0, + 4, + 8, + 9, + 6, + 20, + 4, + 4, + 9, + 20, + 10, + 10, + 0 + ], + [ + 10, + 7, + 12, + 14, + 4, + 2, + 13, + 6, + 11, + 10, + 5, + 3, + 5, + 7, + 17, + 58, + 14, + 1 + ], + [ + 9, + 12, + 21, + 5, + 7, + 7, + 39, + 17, + 8, + 5, + 7, + 6, + 11, + 23, + 11, + 11, + 0 + ], + [ + 8, + 7, + 10, + 5, + 4, + 8, + 23, + 15, + 56, + 5, + 7, + 9, + 17, + 11, + 12, + 2 + ], + [ + 14, + 14, + 6, + 6, + 10, + 49, + 8, + 6, + 6, + 6, + 11, + 25, + 13, + 25, + 0 + ], + [ + 18, + 63, + 7, + 7, + 8, + 12, + 10, + 8, + 10, + 7, + 9, + 21, + 18, + 1 + ], + [ + 18, + 18, + 8, + 9, + 16, + 25, + 13, + 57, + 9, + 9, + 17, + 0 + ], + [ + 62, + 10, + 6, + 10, + 33, + 22, + 8, + 6, + 9, + 10, + 22, + 1 + ], + [ + 24, + 11, + 19, + 29, + 64, + 8, + 11, + 14, + 19, + 0 + ], + [ + 22, + 11, + 21, + 16, + 14, + 13, + 13, + 59, + 28, + 2 + ], + [ + 95, + 14, + 18, + 14, + 14, + 15, + 29, + 0 + ], + [ + 57, + 21, + 16, + 17, + 15, + 17, + 49, + 7 + ], + [ + 63, + 13, + 20, + 13, + 66, + 24, + 0 + ], + [ + 17, + 18, + 18, + 20, + 82, + 44 + ], + [ + 42, + 104, + 18, + 6, + 29, + 0 + ], + [ + 38, + 36, + 86, + 13, + 12, + 13, + 1 + ], + [ + 13, + 8, + 137, + 12, + 22, + 7, + 0 + ], + [ + 39, + 28, + 132 + ], + [ + 62, + 53, + 81, + 3, + 0 + ], + [ + 11, + 188 + ], + [ + 85, + 72, + 42, + 0 + ], + [ + 10, + 189 + ], + [ + 3, + 0, + 196 + ] +] \ No newline at end of file diff --git a/trigo-web/tools/output/selfplay-mcts-5x5/game_1_visit_counts.json b/trigo-web/tools/output/selfplay-mcts-5x5/game_1_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..91c27cae327e4b750a7ecfa6ad70ba84a6ab57fa --- /dev/null +++ b/trigo-web/tools/output/selfplay-mcts-5x5/game_1_visit_counts.json @@ -0,0 +1,454 @@ +[ + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0 + ], + [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 7, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0 + ], + [ + 0, + 0, + 0, + 0, + 4, + 0, + 0, + 0, + 0, + 0, + 0, + 3, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0 + ], + [ + 1, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 3, + 0, + 0, + 5 + ], + [ + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0 + ], + [ + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 5, + 0, + 0, + 0, + 0, + 0, + 0, + 3, + 0, + 0 + ], + [ + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 2, + 1, + 1, + 0 + ], + [ + 0, + 0, + 2, + 0, + 4, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3, + 0, + 0 + ], + [ + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 3, + 0 + ], + [ + 2, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 6, + 0, + 0, + 0, + 0, + 0 + ], + [ + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 2, + 0, + 0, + 1, + 0 + ], + [ + 0, + 4, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0 + ], + [ + 1, + 1, + 3, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0 + ], + [ + 1, + 1, + 0, + 0, + 0, + 5, + 1, + 0, + 0, + 1, + 0 + ], + [ + 1, + 3, + 1, + 0, + 1, + 1, + 1, + 1, + 0 + ], + [ + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 5, + 1, + 0 + ], + [ + 4, + 2, + 0, + 1, + 1, + 1, + 0 + ], + [ + 2, + 0, + 0, + 5, + 0, + 0, + 0, + 2, + 0 + ], + [ + 5, + 1, + 1, + 2, + 0 + ], + [ + 0, + 0, + 2, + 5, + 0, + 2, + 0 + ], + [ + 3, + 1, + 1, + 2, + 2, + 0 + ], + [ + 0, + 2, + 4, + 0, + 1, + 0, + 2, + 0 + ], + [ + 1, + 4, + 2, + 2, + 0 + ], + [ + 0, + 1, + 1, + 2, + 2, + 3 + ], + [ + 2, + 5, + 2 + ], + [ + 0, + 2, + 3, + 2, + 2, + 0 + ], + [ + 9 + ], + [ + 4, + 1, + 2, + 2, + 0 + ], + [ + 9 + ], + [ + 0, + 1, + 1, + 7 + ] +] \ No newline at end of file diff --git a/trigo-web/tools/output/selfplay-mcts/game_1_visit_counts.json b/trigo-web/tools/output/selfplay-mcts/game_1_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..33ba5854bb9ffe34cd7ed5aba46a249e362fec49 --- /dev/null +++ b/trigo-web/tools/output/selfplay-mcts/game_1_visit_counts.json @@ -0,0 +1,1445 @@ +[ + [ + 2, + 2, + 2, + 1, + 5, + 4, + 2, + 1, + 2, + 2, + 1, + 3, + 2, + 1, + 1, + 1, + 1, + 0, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 12, + 1, + 1, + 1, + 1, + 1, + 2, + 4, + 3, + 2, + 5, + 3, + 2, + 1, + 2, + 2, + 1, + 4, + 2, + 2, + 0 + ], + [ + 5, + 4, + 2, + 4, + 2, + 5, + 3, + 0, + 0, + 0, + 0, + 0, + 3, + 0, + 0, + 0, + 3, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8, + 0, + 2, + 22, + 0, + 0, + 0, + 1, + 2, + 2, + 0, + 0, + 0, + 0, + 1, + 3, + 6, + 5, + 2, + 0, + 3, + 5, + 6, + 0 + ], + [ + 4, + 4, + 3, + 2, + 4, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 27, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 0 + ], + [ + 4, + 4, + 3, + 2, + 2, + 5, + 2, + 2, + 0, + 0, + 0, + 2, + 2, + 2, + 2, + 0, + 0, + 2, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 1, + 1, + 3, + 2, + 2, + 2, + 0, + 0, + 1, + 2, + 2, + 4, + 3, + 2, + 2, + 2, + 3, + 29, + 0 + ], + [ + 3, + 4, + 2, + 2, + 3, + 3, + 2, + 2, + 2, + 1, + 5, + 2, + 2, + 1, + 1, + 9, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 4, + 2, + 7, + 2, + 2, + 2, + 3, + 0 + ], + [ + 10, + 5, + 3, + 3, + 6, + 2, + 2, + 0, + 0, + 1, + 2, + 2, + 1, + 0, + 0, + 2, + 0, + 1, + 0, + 0, + 0, + 2, + 0, + 0, + 10, + 1, + 0, + 0, + 0, + 4, + 2, + 2, + 2, + 0, + 0, + 1, + 2, + 2, + 5, + 5, + 2, + 6, + 8, + 5, + 0 + ], + [ + 3, + 2, + 1, + 2, + 3, + 2, + 1, + 1, + 1, + 1, + 9, + 2, + 1, + 9, + 1, + 1, + 1, + 1, + 1, + 1, + 13, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 3, + 2, + 2, + 2, + 1, + 9, + 1, + 1, + 2, + 3, + 3, + 2, + 1, + 2, + 2, + 0 + ], + [ + 10, + 7, + 4, + 3, + 9, + 3, + 3, + 2, + 2, + 2, + 3, + 3, + 3, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 10, + 3, + 0, + 0, + 0, + 0, + 0, + 1, + 2, + 2, + 0, + 5, + 1, + 1, + 6, + 4, + 3, + 2, + 3, + 0 + ], + [ + 31, + 14, + 2, + 3, + 4, + 5, + 1, + 0, + 1, + 1, + 2, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 6, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 4, + 1, + 1, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 3, + 1, + 3, + 0 + ], + [ + 6, + 5, + 5, + 5, + 6, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 1, + 15, + 1, + 2, + 1, + 2, + 2, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1 + ], + [ + 10, + 4, + 2, + 3, + 8, + 4, + 1, + 0, + 0, + 0, + 0, + 3, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12, + 0, + 0, + 0, + 0, + 11, + 1, + 0, + 2, + 21, + 9, + 6, + 2, + 0 + ], + [ + 7, + 4, + 2, + 3, + 6, + 5, + 2, + 1, + 2, + 1, + 2, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 24, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 2, + 1, + 2, + 2, + 4, + 1, + 2, + 1 + ], + [ + 11, + 4, + 6, + 8, + 4, + 4, + 0, + 0, + 0, + 4, + 4, + 0, + 0, + 0, + 4, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5, + 26, + 0, + 4, + 5, + 5, + 5, + 0 + ], + [ + 11, + 6, + 6, + 8, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 29, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 3, + 3, + 1 + ], + [ + 18, + 11, + 5, + 5, + 4, + 5, + 0, + 2, + 2, + 2, + 3, + 8, + 0, + 0, + 0, + 0, + 3, + 3, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 1, + 0, + 2, + 3, + 2, + 8, + 3, + 4, + 3, + 0 + ], + [ + 9, + 4, + 4, + 7, + 3, + 2, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 28, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 7, + 4, + 0 + ], + [ + 7, + 4, + 5, + 7, + 4, + 3, + 2, + 2, + 3, + 3, + 2, + 1, + 1, + 0, + 0, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 22, + 0, + 2, + 2, + 2, + 3, + 2, + 3, + 4, + 3, + 0 + ], + [ + 8, + 10, + 14, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 36, + 0, + 1, + 1, + 2, + 1, + 2, + 2, + 6, + 7, + 0 + ], + [ + 5, + 6, + 4, + 3, + 2, + 3, + 3, + 3, + 2, + 1, + 1, + 1, + 1, + 1, + 10, + 3, + 1, + 2, + 19, + 3, + 2, + 1, + 1, + 2, + 2, + 3, + 3, + 3, + 4, + 4, + 0 + ], + [ + 3, + 4, + 7, + 4, + 1, + 1, + 4, + 22, + 5, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 4, + 2, + 1, + 10, + 16, + 3, + 4, + 0 + ], + [ + 3, + 4, + 11, + 3, + 2, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 3, + 2, + 2, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 3, + 3, + 3, + 16 + ], + [ + 5, + 6, + 4, + 2, + 3, + 4, + 5, + 2, + 1, + 0, + 0, + 1, + 2, + 19, + 0, + 0, + 0, + 18, + 2, + 0, + 2, + 2, + 4, + 3, + 2, + 4, + 4, + 4, + 0 + ], + [ + 4, + 15, + 3, + 2, + 3, + 3, + 3, + 2, + 2, + 2, + 4, + 2, + 2, + 3, + 3, + 2, + 3, + 3, + 15, + 2, + 3, + 3, + 5, + 3, + 3, + 3, + 1 + ], + [ + 3, + 5, + 5, + 3, + 4, + 5, + 6, + 2, + 0, + 0, + 0, + 0, + 2, + 15, + 0, + 0, + 2, + 15, + 6, + 3, + 3, + 4, + 3, + 4, + 4, + 5, + 0 + ], + [ + 5, + 6, + 3, + 2, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 6, + 3, + 3, + 3, + 3, + 17, + 3, + 3, + 3, + 13, + 1 + ], + [ + 4, + 5, + 5, + 3, + 3, + 5, + 6, + 3, + 30, + 0, + 0, + 2, + 3, + 2, + 0, + 0, + 2, + 3, + 3, + 4, + 3, + 4, + 4, + 5, + 0 + ], + [ + 7, + 9, + 4, + 3, + 3, + 4, + 4, + 2, + 3, + 1, + 1, + 3, + 3, + 2, + 3, + 3, + 3, + 2, + 4, + 3, + 4, + 5, + 23 + ], + [ + 4, + 5, + 3, + 4, + 5, + 6, + 3, + 0, + 0, + 5, + 3, + 3, + 11, + 0, + 2, + 17, + 3, + 5, + 4, + 5, + 5, + 6, + 0 + ], + [ + 4, + 3, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 4, + 4, + 2, + 35, + 4, + 3, + 4, + 4, + 4, + 4, + 4, + 0 + ], + [ + 5, + 4, + 5, + 6, + 4, + 3, + 18, + 3, + 4, + 5, + 2, + 2, + 7, + 3, + 3, + 5, + 4, + 5, + 5, + 6, + 0 + ], + [ + 19, + 4, + 7, + 4, + 4, + 3, + 3, + 4, + 11, + 2, + 4, + 4, + 6, + 5, + 5, + 5, + 5, + 4, + 0 + ], + [ + 4, + 6, + 4, + 32, + 3, + 6, + 3, + 2, + 1, + 2, + 2, + 2, + 8, + 6, + 3, + 5, + 4, + 6, + 0 + ], + [ + 7, + 4, + 26, + 4, + 4, + 3, + 3, + 4, + 4, + 6, + 4, + 4, + 4, + 6, + 4, + 4, + 4, + 4, + 0 + ], + [ + 9, + 7, + 5, + 6, + 2, + 22, + 4, + 3, + 2, + 2, + 3, + 3, + 4, + 5, + 4, + 5, + 9, + 4, + 0 + ], + [ + 8, + 5, + 4, + 4, + 4, + 7, + 26, + 2, + 4, + 3, + 3, + 10, + 5, + 5, + 5, + 4, + 0 + ], + [ + 8, + 7, + 28, + 6, + 3, + 8, + 2, + 2, + 3, + 4, + 4, + 5, + 4, + 6, + 4, + 5, + 0 + ], + [ + 9, + 6, + 14, + 4, + 5, + 3, + 5, + 4, + 5, + 7, + 14, + 5, + 13, + 5, + 0 + ], + [ + 10, + 9, + 3, + 4, + 2, + 2, + 27, + 4, + 7, + 8, + 4, + 6, + 6, + 7, + 0 + ], + [ + 12, + 10, + 5, + 3, + 5, + 3, + 5, + 5, + 4, + 5, + 5, + 5, + 5, + 27 + ], + [ + 35, + 7, + 4, + 4, + 2, + 3, + 3, + 6, + 7, + 7, + 6, + 7, + 8, + 0 + ], + [ + 16, + 4, + 4, + 28, + 2, + 3, + 9, + 9, + 6, + 8, + 10, + 0 + ], + [ + 9, + 10, + 4, + 6, + 4, + 8, + 17, + 7, + 7, + 7, + 20, + 0 + ], + [ + 31, + 6, + 10, + 11, + 8, + 10, + 6, + 6, + 6, + 5, + 0 + ], + [ + 10, + 6, + 5, + 12, + 4, + 22, + 15, + 8, + 9, + 8, + 0 + ], + [ + 11, + 14, + 9, + 9, + 10, + 8, + 9, + 7, + 22 + ], + [ + 10, + 11, + 6, + 6, + 5, + 10, + 9, + 9, + 33, + 0 + ], + [ + 6, + 11, + 7, + 10, + 40, + 10, + 15, + 0 + ], + [ + 12, + 12, + 9, + 8, + 9, + 12, + 37, + 0 + ], + [ + 9, + 15, + 7, + 37, + 31, + 0 + ], + [ + 21, + 27, + 15, + 16, + 20, + 0 + ], + [ + 49, + 9, + 41, + 0 + ], + [ + 22, + 13, + 64 + ], + [ + 5, + 1, + 4, + 89, + 0 + ], + [ + 99 + ], + [ + 0, + 98, + 1, + 0 + ] +] \ No newline at end of file diff --git a/trigo-web/tools/output/selfplay_7x1x1_test/game_10_visit_counts.json b/trigo-web/tools/output/selfplay_7x1x1_test/game_10_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..eaff9f790528c979c6314ac5cd243fd19bd89f01 --- /dev/null +++ b/trigo-web/tools/output/selfplay_7x1x1_test/game_10_visit_counts.json @@ -0,0 +1,72 @@ +[ + [ + 26, + 8, + 17, + 15, + 18, + 8, + 7, + 0 + ], + [ + 8, + 34, + 8, + 19, + 20, + 10, + 0 + ], + [ + 47, + 23, + 3, + 4, + 6, + 16 + ], + [ + 85, + 7, + 4, + 3 + ], + [ + 5, + 9, + 3, + 81, + 1 + ], + [ + 10, + 14, + 61, + 14, + 0 + ], + [ + 39, + 32, + 28 + ], + [ + 90, + 9, + 0 + ], + [ + 95, + 4, + 0 + ], + [ + 99 + ], + [ + 13, + 86, + 0 + ] +] \ No newline at end of file diff --git a/trigo-web/tools/output/selfplay_7x1x1_test/game_1_visit_counts.json b/trigo-web/tools/output/selfplay_7x1x1_test/game_1_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..678518aff658b91f663086986e5a6b00a9d5f4f8 --- /dev/null +++ b/trigo-web/tools/output/selfplay_7x1x1_test/game_1_visit_counts.json @@ -0,0 +1,60 @@ +[ + [ + 6, + 8, + 16, + 14, + 18, + 8, + 6, + 23 + ], + [ + 33, + 11, + 14, + 10, + 9, + 10, + 12, + 0 + ], + [ + 11, + 12, + 10, + 17, + 27, + 22, + 0 + ], + [ + 5, + 2, + 89, + 3, + 0 + ], + [ + 73, + 11, + 2, + 9, + 4 + ], + [ + 43, + 10, + 46, + 0 + ], + [ + 4, + 95 + ], + [ + 2, + 5, + 92 + ] +] \ No newline at end of file diff --git a/trigo-web/tools/output/selfplay_7x1x1_test/game_2_visit_counts.json b/trigo-web/tools/output/selfplay_7x1x1_test/game_2_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..1488e1a3157e5badf3db767de35f58f6e1664fa3 --- /dev/null +++ b/trigo-web/tools/output/selfplay_7x1x1_test/game_2_visit_counts.json @@ -0,0 +1,63 @@ +[ + [ + 26, + 8, + 17, + 15, + 18, + 8, + 7, + 0 + ], + [ + 15, + 26, + 15, + 14, + 16, + 13, + 0 + ], + [ + 46, + 16, + 17, + 20, + 0 + ], + [ + 3, + 90, + 6, + 0 + ], + [ + 10, + 84, + 5 + ], + [ + 16, + 83, + 0 + ], + [ + 3, + 41, + 55 + ], + [ + 11, + 7, + 81, + 0 + ], + [ + 99 + ], + [ + 1, + 1, + 97 + ] +] \ No newline at end of file diff --git a/trigo-web/tools/output/selfplay_7x1x1_test/game_3_visit_counts.json b/trigo-web/tools/output/selfplay_7x1x1_test/game_3_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..79ec60bc531d4a143f1191425cae8bf18e1241b7 --- /dev/null +++ b/trigo-web/tools/output/selfplay_7x1x1_test/game_3_visit_counts.json @@ -0,0 +1,57 @@ +[ + [ + 6, + 27, + 17, + 15, + 18, + 9, + 7, + 0 + ], + [ + 21, + 11, + 11, + 49, + 7, + 0 + ], + [ + 20, + 12, + 18, + 3, + 40, + 6 + ], + [ + 35, + 36, + 28 + ], + [ + 2, + 93, + 4 + ], + [ + 44, + 33, + 22 + ], + [ + 3, + 4, + 92 + ], + [ + 98, + 1 + ], + [ + 4, + 93, + 2 + ] +] \ No newline at end of file diff --git a/trigo-web/tools/output/selfplay_7x1x1_test/game_4_visit_counts.json b/trigo-web/tools/output/selfplay_7x1x1_test/game_4_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..1731d7dab1798233388dd6e764e20fa7b6084437 --- /dev/null +++ b/trigo-web/tools/output/selfplay_7x1x1_test/game_4_visit_counts.json @@ -0,0 +1,50 @@ +[ + [ + 7, + 8, + 18, + 15, + 18, + 9, + 24, + 0 + ], + [ + 9, + 25, + 7, + 27, + 22, + 9, + 0 + ], + [ + 3, + 87, + 4, + 5, + 0 + ], + [ + 35, + 2, + 12, + 48, + 2 + ], + [ + 10, + 89, + 0 + ], + [ + 89, + 8, + 2 + ], + [ + 92, + 6, + 1 + ] +] \ No newline at end of file diff --git a/trigo-web/tools/output/selfplay_7x1x1_test/game_5_visit_counts.json b/trigo-web/tools/output/selfplay_7x1x1_test/game_5_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..7eefba42e51e24e589c8b365d106ee360f19a19b --- /dev/null +++ b/trigo-web/tools/output/selfplay_7x1x1_test/game_5_visit_counts.json @@ -0,0 +1,57 @@ +[ + [ + 6, + 7, + 39, + 13, + 16, + 12, + 6, + 0 + ], + [ + 9, + 32, + 7, + 27, + 8, + 9, + 7 + ], + [ + 11, + 7, + 77, + 2, + 2, + 0 + ], + [ + 29, + 3, + 2, + 38, + 27 + ], + [ + 2, + 7, + 2, + 2, + 86, + 0 + ], + [ + 43, + 1, + 3, + 52 + ], + [ + 34, + 44, + 11, + 10, + 0 + ] +] \ No newline at end of file diff --git a/trigo-web/tools/output/selfplay_7x1x1_test/game_6_visit_counts.json b/trigo-web/tools/output/selfplay_7x1x1_test/game_6_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..07eab4f8a89653c99faf85b30d9c950d98004844 --- /dev/null +++ b/trigo-web/tools/output/selfplay_7x1x1_test/game_6_visit_counts.json @@ -0,0 +1,63 @@ +[ + [ + 7, + 8, + 18, + 15, + 18, + 9, + 24, + 0 + ], + [ + 19, + 15, + 7, + 12, + 14, + 32, + 0 + ], + [ + 10, + 28, + 13, + 11, + 37, + 0 + ], + [ + 70, + 2, + 8, + 19 + ], + [ + 3, + 3, + 48, + 30, + 15, + 0 + ], + [ + 84, + 3, + 12 + ], + [ + 12, + 15, + 72, + 0 + ], + [ + 10, + 89 + ], + [ + 87, + 12, + 0 + ] +] \ No newline at end of file diff --git a/trigo-web/tools/output/selfplay_7x1x1_test/game_7_visit_counts.json b/trigo-web/tools/output/selfplay_7x1x1_test/game_7_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..2ac0aec53ca5a65cbc0bb986bdc055a5efbf49e8 --- /dev/null +++ b/trigo-web/tools/output/selfplay_7x1x1_test/game_7_visit_counts.json @@ -0,0 +1,44 @@ +[ + [ + 6, + 8, + 18, + 14, + 17, + 8, + 6, + 22 + ], + [ + 13, + 13, + 12, + 37, + 12, + 12, + 0 + ], + [ + 13, + 18, + 41, + 4, + 23, + 0 + ], + [ + 4, + 23, + 19, + 33, + 20 + ], + [ + 95, + 2, + 2, + 0 + ], + [], + [] +] \ No newline at end of file diff --git a/trigo-web/tools/output/selfplay_7x1x1_test/game_8_visit_counts.json b/trigo-web/tools/output/selfplay_7x1x1_test/game_8_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..72595889de56110b5b06a0b698f6de556c8b5366 --- /dev/null +++ b/trigo-web/tools/output/selfplay_7x1x1_test/game_8_visit_counts.json @@ -0,0 +1,49 @@ +[ + [ + 6, + 8, + 17, + 15, + 39, + 8, + 6, + 0 + ], + [ + 8, + 7, + 31, + 15, + 20, + 18, + 0 + ], + [ + 3, + 2, + 84, + 3, + 7 + ], + [ + 55, + 12, + 2, + 26, + 4 + ], + [ + 99, + 0 + ], + [ + 7, + 88, + 4 + ], + [ + 8, + 91, + 0 + ] +] \ No newline at end of file diff --git a/trigo-web/tools/output/selfplay_7x1x1_test/game_9_visit_counts.json b/trigo-web/tools/output/selfplay_7x1x1_test/game_9_visit_counts.json new file mode 100644 index 0000000000000000000000000000000000000000..4ab39f0711c6a062a19d3979877396a34804dd04 --- /dev/null +++ b/trigo-web/tools/output/selfplay_7x1x1_test/game_9_visit_counts.json @@ -0,0 +1,51 @@ +[ + [ + 6, + 8, + 17, + 15, + 39, + 8, + 6, + 0 + ], + [ + 20, + 15, + 14, + 13, + 17, + 20, + 0 + ], + [ + 17, + 10, + 62, + 10, + 0 + ], + [ + 90, + 7, + 2 + ], + [ + 72, + 20, + 6, + 1 + ], + [ + 86, + 2, + 11 + ], + [ + 41, + 16, + 10, + 32, + 0 + ] +] \ No newline at end of file diff --git a/trigo-web/tools/selfPlayGames.ts b/trigo-web/tools/selfPlayGames.ts new file mode 100644 index 0000000000000000000000000000000000000000..49883369166deccf396ca322cad70a7e1989817b --- /dev/null +++ b/trigo-web/tools/selfPlayGames.ts @@ -0,0 +1,766 @@ +/** + * Self-Play Dataset Generation for Trigo AI + * + * This script generates a dataset of self-play games for training the Trigo AI model. + * The AI plays against itself using either tree attention or MCTS-based agents. + * + * Features: + * - Configurable number of games to generate + * - Random or fixed board shapes (2D: 2x1x1 to 5x5x1, 3D: 2x2x2 to 3x3x3) + * - Temperature-based sampling for move diversity + * - MCTS (Monte Carlo Tree Search) mode with AlphaGo Zero algorithm + * - Automatic game termination detection (50% coverage threshold) + * - TGN format output with score notation for each game + * - Optional visit count statistics for MCTS training data + * - Per-board-shape statistics + * - Progress tracking + * + * Usage: + * npx tsx tools/selfPlayGames.ts [options] + * + * Options: + * --games Number of games to generate (default: 10) + * --output Output directory (default: ./tools/output/selfplay) + * --board Board shape "X*Y*Z" or "random" (default: "random") + * --temperature Sampling temperature (default: 1.0) + * --max-moves Maximum moves per game (default: 300) + * --model Path to tree model ONNX file + * --eval-model Path to evaluation model ONNX file (for MCTS) + * --verbose Enable verbose logging + * + * MCTS Options: + * --use-mcts Enable MCTS for move selection + * --mcts-simulations MCTS simulations per move (default: 600) + * --mcts-cpuct PUCT exploration constant (default: 1.0) + * --mcts-dirichlet-alpha Dirichlet noise alpha (default: 0.03) + * --mcts-dirichlet-epsilon Dirichlet noise epsilon (default: 0.25) + * --save-visit-counts Save visit count statistics + * + * Output: + * - Each game saved as game_.tgn (hash based on content) + * - Dataset statistics in _dataset_stats.json + * - Visit counts saved as game__visit_counts.json (if --save-visit-counts) + */ + +import * as ort from "onnxruntime-node"; +import * as path from "path"; +import * as fs from "fs"; +import * as crypto from "crypto"; +import { fileURLToPath } from "url"; +import { TrigoGame, StoneType } from "../inc/trigo/game"; +import { ModelInferencer } from "../inc/modelInferencer"; +import { loadEnvConfig, getOnnxModelPaths, getAbsoluteModelPath, getOnnxSessionOptions } from "../inc/config"; +import { TrigoTreeAgent } from "../inc/trigoTreeAgent"; +import { TrigoEvaluationAgent } from "../inc/trigoEvaluationAgent"; +import { MCTSAgent, type MCTSConfig } from "../inc/mctsAgent"; +import type { Move, BoardShape } from "../inc/trigo/types"; +import {encodeAb0yz} from "../inc/trigo/ab0yz"; + + +// ES module equivalent of __dirname +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Load environment variables +await loadEnvConfig(); + +// Default model paths from environment +const defaultModelPaths = getOnnxModelPaths(); + + +// Board shape types +type BoardShapeTuple = [number, number, number]; + + +/** + * Generate all board shapes in a range (inclusive) + */ +const arangeShape = (min: BoardShapeTuple, max: BoardShapeTuple): BoardShapeTuple[] => { + const result: BoardShapeTuple[] = []; + + for (let x = min[0]; x <= max[0]; x++) { + for (let y = min[1]; y <= max[1]; y++) { + for (let z = min[2]; z <= max[2]; z++) { + result.push([x, y, z]); + } + } + } + + return result; +}; + + +/** + * Candidate board shapes for random selection + * - 2D boards: 2x1x1 to 19x19x1 + * - 3D boards: 2x2x2 to 9x9x9 + */ +const CANDIDATE_BOARD_SHAPES = [ + ...arangeShape([2, 1, 1], [13, 13, 1]), + ...arangeShape([2, 2, 2], [5, 5, 5]), +]; + + +// Configuration +interface GenerationConfig { + numGames: number; + outputDir: string; + temperature: number; + maxMoves: number; + verbose: boolean; + modelPath: string; + evaluationModelPath: string; + vocabSize: number; + seqLen: number; + boardShape: BoardShape | "random"; + // MCTS configuration + useMCTS: boolean; + mctsSimulations: number; + mctsCPuct: number; + mctsDirichletAlpha: number; + mctsDirichletEpsilon: number; + saveVisitCounts: boolean; +} + + +// Game statistics +interface GameStats { + gameId: number; + boardShape: string; + moveCount: number; + maxMovesReached: boolean; + duration: number; // milliseconds + averageMoveTime: number; // milliseconds + scoreDiff: number; // white - black (positive: white wins, negative: black wins) +} + + +// Board shape statistics +interface BoardShapeStats { + boardShape: string; + gameCount: number; + averageScoreDiff: number; + averageMoveCount: number; +} + + +// Dataset statistics +interface DatasetStats { + totalGames: number; + totalMoves: number; + blackWins: number; + whiteWins: number; + resignations: number; + maxMovesReached: number; + averageGameLength: number; + averageMoveTime: number; + generationTime: number; // milliseconds + averageScoreDiff: number; // average white - black + boardShapeStats: BoardShapeStats[]; // stats per board shape + games: GameStats[]; +} + + +/** + * Parse board shape string (e.g., "5*5*5" or "9*9*1") + * Special value "random" selects randomly from CANDIDATE_BOARD_SHAPES + */ +function parseBoardShape(shapeStr: string): BoardShape | "random" { + // Handle random selection + if (shapeStr.toLowerCase() === "random") { + return "random"; + } + + // Parse explicit board shape + const parts = shapeStr.split(/[^0-9]+/).filter(Boolean).map(Number); + if (parts.length !== 3) { + throw new Error(`Invalid board shape: ${shapeStr}. Expected format: "X*Y*Z" or "random"`); + } + return { x: parts[0], y: parts[1], z: parts[2] }; +} + + +/** + * Select a random board shape from candidates + */ +function selectRandomBoardShape(): BoardShape { + const randomIndex = Math.floor(Math.random() * CANDIDATE_BOARD_SHAPES.length); + const [x, y, z] = CANDIDATE_BOARD_SHAPES[randomIndex]; + return { x, y, z }; +} + + +/** + * Parse command line arguments + */ +function parseArgs(): GenerationConfig { + const args = process.argv.slice(2); + const config: GenerationConfig = { + numGames: 10, + outputDir: path.join(__dirname, "output/selfplay"), + temperature: 1.0, + maxMoves: 300, + verbose: false, + modelPath: getAbsoluteModelPath(defaultModelPaths.treeModel), + evaluationModelPath: getAbsoluteModelPath(defaultModelPaths.evaluationModel), + vocabSize: 128, + seqLen: 256, + boardShape: "random", + // MCTS defaults + useMCTS: false, + mctsSimulations: 600, + mctsCPuct: 1.0, + mctsDirichletAlpha: 0.03, + mctsDirichletEpsilon: 0.25, + saveVisitCounts: false + }; + + for (let i = 0; i < args.length; i++) { + switch (args[i]) { + case "--games": + config.numGames = parseInt(args[++i], 10); + break; + case "--output": + config.outputDir = args[++i]; + break; + case "--temperature": + config.temperature = parseFloat(args[++i]); + break; + case "--max-moves": + config.maxMoves = parseInt(args[++i], 10); + break; + case "--board": + config.boardShape = parseBoardShape(args[++i]); + break; + case "--model": + config.modelPath = args[++i]; + break; + case "--eval-model": + config.evaluationModelPath = args[++i]; + break; + case "--use-mcts": + config.useMCTS = true; + break; + case "--mcts-simulations": + config.mctsSimulations = parseInt(args[++i], 10); + break; + case "--mcts-cpuct": + config.mctsCPuct = parseFloat(args[++i]); + break; + case "--mcts-dirichlet-alpha": + config.mctsDirichletAlpha = parseFloat(args[++i]); + break; + case "--mcts-dirichlet-epsilon": + config.mctsDirichletEpsilon = parseFloat(args[++i]); + break; + case "--save-visit-counts": + config.saveVisitCounts = true; + break; + case "--verbose": + config.verbose = true; + break; + case "--help": + printHelp(); + process.exit(0); + default: + if (args[i].startsWith("--")) { + console.error(`Unknown option: ${args[i]}`); + printHelp(); + process.exit(1); + } + } + } + + return config; +} + + +/** + * Print help message + */ +function printHelp(): void { + console.log(` +Usage: npx tsx tools/selfPlayGames.ts [options] + +Options: + --games Number of games to generate (default: 10) + --output Output directory (default: ./tools/output/selfplay) + --board Board shape "X*Y*Z" or "random" (default: "random") + --temperature Sampling temperature (default: 1.0) + --max-moves Maximum moves per game (default: 300) + --model Path to tree model ONNX file + --eval-model Path to evaluation model ONNX file (for MCTS) + --verbose Enable verbose logging + + MCTS Options: + --use-mcts Enable MCTS for move selection + --mcts-simulations MCTS simulations per move (default: 600) + --mcts-cpuct PUCT exploration constant (default: 1.0) + --mcts-dirichlet-alpha Dirichlet noise alpha (default: 0.03) + --mcts-dirichlet-epsilon Dirichlet noise epsilon (default: 0.25) + --save-visit-counts Save visit count statistics + --help Show this help message + +Board Shape Examples: + --board "5*5*5" Fixed 5x5x5 board for all games + --board "9*9*1" Fixed 9x9x1 (2D) board for all games + --board random Random board shape for each game (default) + +Examples: + # Generate 100 games with random board shapes (no MCTS) + npx tsx tools/selfPlayGames.ts --games 100 + + # Generate 50 games on 5x5x5 board with MCTS + npx tsx tools/selfPlayGames.ts --games 50 --board "5*5*5" --use-mcts --mcts-simulations 600 + + # Generate games with custom models + npx tsx tools/selfPlayGames.ts --games 20 --model ./models/tree.onnx --eval-model ./models/eval.onnx + + # Generate games with custom output directory and save visit counts + npx tsx tools/selfPlayGames.ts --games 20 --output ./my_dataset --use-mcts --save-visit-counts +`); +} + + +/** + * Initialize the AI agent (tree agent or MCTS agent) + */ +async function initializeAgent(config: GenerationConfig): Promise { + console.log("Initializing AI Agent..."); + console.log(` Mode: ${config.useMCTS ? "MCTS Search" : "Tree Attention"}`); + console.log(` Tree Model: ${config.modelPath}`); + if (config.useMCTS) { + console.log(` Evaluation Model: ${config.evaluationModelPath}`); + console.log(` MCTS Simulations: ${config.mctsSimulations}`); + console.log(` C-PUCT: ${config.mctsCPuct}`); + } + console.log(` Vocab Size: ${config.vocabSize}`); + console.log(` Sequence Length: ${config.seqLen}`); + + // Load tree model + const sessionOptions = getOnnxSessionOptions(); + const treeSession = await ort.InferenceSession.create(config.modelPath, sessionOptions); + + const treeInferencer = new ModelInferencer(ort.Tensor as any, { + vocabSize: config.vocabSize, + seqLen: config.seqLen, + modelPath: config.modelPath + }); + treeInferencer.setSession(treeSession as any); + + const treeAgent = new TrigoTreeAgent(treeInferencer); + + // Return tree agent if MCTS is disabled + if (!config.useMCTS) { + console.log("✓ Tree Agent initialized\n"); + return treeAgent; + } + + // Load evaluation model for MCTS + const evalSession = await ort.InferenceSession.create(config.evaluationModelPath, sessionOptions); + + const evalInferencer = new ModelInferencer(ort.Tensor as any, { + vocabSize: config.vocabSize, + seqLen: config.seqLen, + modelPath: config.evaluationModelPath + }); + evalInferencer.setSession(evalSession as any); + + const evaluationAgent = new TrigoEvaluationAgent(evalInferencer); + + // Create MCTS agent + const mctsConfig: MCTSConfig = { + numSimulations: config.mctsSimulations, + cPuct: config.mctsCPuct, + temperature: config.temperature, + dirichletAlpha: config.mctsDirichletAlpha, + dirichletEpsilon: config.mctsDirichletEpsilon + }; + + const mctsAgent = new MCTSAgent(treeAgent, evaluationAgent, mctsConfig); + console.log("✓ MCTS Agent initialized\n"); + + return mctsAgent; +} + + +/** + * Sample a move from probability distribution with temperature + */ +function sampleMove(scoredMoves: Array<{ move: Move; score: number; notation: string }>, temperature: number): Move { + // Apply temperature to scores (log probabilities) + const adjustedScores = scoredMoves.map((m) => m.score / temperature); + + // Convert to probabilities using softmax + const maxScore = Math.max(...adjustedScores); + const expScores = adjustedScores.map((score) => Math.exp(score - maxScore)); + const sumExp = expScores.reduce((sum, exp) => sum + exp, 0); + const probabilities = expScores.map((exp) => exp / sumExp); + + // Sample from distribution + const random = Math.random(); + let cumulative = 0; + + for (let i = 0; i < scoredMoves.length; i++) { + cumulative += probabilities[i]; + if (random <= cumulative) { + return scoredMoves[i].move; + } + } + + // Fallback to last move (should never happen) + return scoredMoves[scoredMoves.length - 1].move; +} + + +/** + * Play a single self-play game + */ +async function playSelfPlayGame( + agent: TrigoTreeAgent | MCTSAgent, + gameId: number, + config: GenerationConfig +): Promise<{ game: TrigoGame; stats: GameStats; visitCounts?: number[][] }> { + // Select board shape (random or fixed) + const boardShape: BoardShape = + config.boardShape === "random" + ? selectRandomBoardShape() + : config.boardShape; + + const game = new TrigoGame(boardShape, {}); + const startTime = Date.now(); + let moveCount = 0; + let totalMoveTime = 0; + let consecutivePasses = 0; + const visitCountsHistory: number[][] = []; + + // Calculate territory check threshold (50% coverage, same as MCTS) + const totalPositions = boardShape.x * boardShape.y * boardShape.z; + const coverageThreshold = Math.floor(totalPositions * 0.5); + let territoryCheckStarted = false; + + if (config.verbose) { + console.log(`\nGame ${gameId} started [Board: ${boardShape.x}×${boardShape.y}×${boardShape.z}]`); + } + + while (moveCount < config.maxMoves) { + // Check if we should start territory checking (after 50% coverage) + if (!territoryCheckStarted && moveCount >= coverageThreshold) { + territoryCheckStarted = true; + if (config.verbose) { + console.log(` Reached 50% coverage (${moveCount} moves), starting territory check`); + } + } + + // Get current player + const currentPlayer = game.getCurrentPlayer() === StoneType.BLACK ? "black" : "white"; + + const moveStartTime = Date.now(); + let selectedMove: Move; + let visitCounts: Map | undefined; + + // Use MCTS or tree agent depending on agent type + if (agent instanceof MCTSAgent) { + // MCTS move selection + const result = await agent.selectMove(game, moveCount); + selectedMove = result.move; + visitCounts = result.visitCounts; + + // Store visit counts if requested + if (config.saveVisitCounts && visitCounts) { + visitCountsHistory.push(Array.from(visitCounts.values())); + } + } else { + // Tree agent move selection (original method) + // Get all valid moves + const validPositions = game.validMovePositions(); + const moves: Move[] = validPositions.map((pos) => ({ + x: pos.x, + y: pos.y, + z: pos.z, + player: currentPlayer + })); + moves.push({ player: currentPlayer, isPass: true }); + + // If no valid moves (only pass), game is over + if (validPositions.length === 0) { + game.pass(); + break; + } + + // Score all moves + const scoredMoves = await agent.scoreMoves(game, moves); + + if (scoredMoves.length === 0) { + break; + } + + // Sort by score + scoredMoves.sort((a, b) => b.score - a.score); + + // Sample move with temperature + selectedMove = sampleMove(scoredMoves, config.temperature); + } + + const moveEndTime = Date.now(); + totalMoveTime += moveEndTime - moveStartTime; + + // Apply move + let success = false; + let moveNotation = ""; + if (selectedMove.isPass) { + success = game.pass(); + moveNotation = "Pass"; + consecutivePasses++; + } else if ( + selectedMove.x !== undefined && + selectedMove.y !== undefined && + selectedMove.z !== undefined + ) { + success = game.drop({ x: selectedMove.x, y: selectedMove.y, z: selectedMove.z }); + moveNotation = encodeAb0yz([selectedMove.x, selectedMove.y, selectedMove.z], [boardShape.x, boardShape.y, boardShape.z]); + consecutivePasses = 0; + } + process.stdout.write(moveNotation + " "); + + if (!success) { + console.error(`Failed to apply move: ${moveNotation}`); + break; + } + + moveCount++; + + if (config.verbose) { + const player = currentPlayer === "black" ? "Black" : "White"; + console.log(` Move ${moveCount}: ${player} plays ${moveNotation}`); + } + + // Check for game end (two consecutive passes) + if (consecutivePasses >= 2) { + if (config.verbose) { + console.log(" Game ended: Two consecutive passes"); + } + break; + } + + // Check territory after 50% coverage (same as MCTS) + if (territoryCheckStarted && !selectedMove.isPass) { + // Check for natural termination (all territory claimed, no capturing moves) + if (game.isNaturallyTerminal()) { + if (config.verbose) { + const territory = game.getTerritory(); + console.log(` Game ended: No neutral territory and no captures possible (settled)`); + console.log(` Black: ${territory.black}, White: ${territory.white}`); + } + break; + } else if (config.verbose) { + const territory = game.getTerritory(); + if (territory.neutral === 0) { + console.log(` Territory settled but captures still possible (continuing...)`); + } + } + } + } + + const endTime = Date.now(); + const duration = endTime - startTime; + const averageMoveTime = moveCount > 0 ? totalMoveTime / moveCount : 0; + + // Determine game result + const maxMovesReached = moveCount >= config.maxMoves; + + // Get final territory and calculate score difference + const territory = game.getTerritory(); + const scoreDiff = territory.white - territory.black; + + const stats: GameStats = { + gameId, + boardShape: `${boardShape.x}×${boardShape.y}×${boardShape.z}`, + moveCount, + maxMovesReached, + duration, + averageMoveTime, + scoreDiff + }; + + return { + game, + stats, + visitCounts: config.saveVisitCounts ? visitCountsHistory : undefined + }; +} + + +/** + * Save game to TGN file using hash-based filename + */ +function saveGame(game: TrigoGame, outputDir: string): string { + const tgn = game.toTGN({}, { markResult: true }); + + // Generate filename based on content hash (same as generateRandomGames.ts) + const hash = crypto.createHash('sha256').update(tgn).digest('hex'); + const filename = `game_${hash.substring(0, 16)}.tgn`; + const filepath = path.join(outputDir, filename); + + fs.writeFileSync(filepath, tgn, "utf-8"); + + return filename; +} + + +/** + * Generate dataset of self-play games + */ +async function generateDataset(config: GenerationConfig): Promise { + console.log("=".repeat(80)); + console.log("Trigo Self-Play Dataset Generation"); + console.log("=".repeat(80)); + console.log(`Configuration:`); + console.log(` Number of games: ${config.numGames}`); + console.log(` Output directory: ${config.outputDir}`); + console.log(` Temperature: ${config.temperature}`); + console.log(` Max moves per game: ${config.maxMoves}`); + console.log(` Verbose: ${config.verbose}`); + console.log(); + + // Create output directory + if (!fs.existsSync(config.outputDir)) { + fs.mkdirSync(config.outputDir, { recursive: true }); + console.log(`✓ Created output directory: ${config.outputDir}\n`); + } + + // Initialize agent + const agent = await initializeAgent(config); + + // Generate games + const startTime = Date.now(); + const datasetStats: DatasetStats = { + totalGames: 0, + totalMoves: 0, + blackWins: 0, + whiteWins: 0, + resignations: 0, + maxMovesReached: 0, + averageGameLength: 0, + averageMoveTime: 0, + generationTime: 0, + averageScoreDiff: 0, + boardShapeStats: [], + games: [] + }; + + console.log("Generating games..."); + console.log("=".repeat(80)); + + for (let i = 1; i <= config.numGames; i++) { + const gameStartTime = Date.now(); + + // Play game + const { game, stats, visitCounts } = await playSelfPlayGame(agent, i, config); + + // Save game with hash-based filename + saveGame(game, config.outputDir); + + // Save visit counts if available + if (visitCounts && config.saveVisitCounts) { + const visitCountsPath = path.join(config.outputDir, `game_${i}_visit_counts.json`); + fs.writeFileSync(visitCountsPath, JSON.stringify(visitCounts, null, 2), "utf-8"); + } + + const gameEndTime = Date.now(); + const gameDuration = gameEndTime - gameStartTime; + + // Update statistics + datasetStats.totalGames++; + datasetStats.totalMoves += stats.moveCount; + if (stats.maxMovesReached) datasetStats.maxMovesReached++; + datasetStats.games.push(stats); + + // Progress update + const progress = ((i / config.numGames) * 100).toFixed(1); + const result = stats.scoreDiff > 0 ? `White +${stats.scoreDiff}` : stats.scoreDiff < 0 ? `Black +${Math.abs(stats.scoreDiff)}` : "Draw"; + console.log( + `[${progress}%] Game ${i}/${config.numGames} [${stats.boardShape}]: ` + + `${stats.moveCount} moves, ${result}, ${(gameDuration / 1000).toFixed(1)}s` + ); + } + + const endTime = Date.now(); + datasetStats.generationTime = endTime - startTime; + datasetStats.averageGameLength = datasetStats.totalMoves / datasetStats.totalGames; + datasetStats.averageMoveTime = + datasetStats.games.reduce((sum, g) => sum + g.averageMoveTime, 0) / datasetStats.totalGames; + + // Calculate average score difference + datasetStats.averageScoreDiff = + datasetStats.games.reduce((sum, g) => sum + g.scoreDiff, 0) / datasetStats.totalGames; + + // Calculate per-board-shape statistics + const shapeMap = new Map(); + for (const game of datasetStats.games) { + if (!shapeMap.has(game.boardShape)) { + shapeMap.set(game.boardShape, []); + } + shapeMap.get(game.boardShape)!.push(game); + } + + datasetStats.boardShapeStats = Array.from(shapeMap.entries()).map(([boardShape, games]) => ({ + boardShape, + gameCount: games.length, + averageScoreDiff: games.reduce((sum, g) => sum + g.scoreDiff, 0) / games.length, + averageMoveCount: games.reduce((sum, g) => sum + g.moveCount, 0) / games.length + })); + + // Sort by board shape for consistent output + datasetStats.boardShapeStats.sort((a, b) => a.boardShape.localeCompare(b.boardShape)); + + // Save dataset statistics with timestamp + const timestamp = new Date().toISOString().replace(/[:.]/g, "-").split("T")[0]; + const statsFilepath = path.join(config.outputDir, `${timestamp}_dataset_stats.json`); + fs.writeFileSync(statsFilepath, JSON.stringify(datasetStats, null, 2), "utf-8"); + + // Print summary + console.log("=".repeat(80)); + console.log("Dataset Generation Complete!"); + console.log("=".repeat(80)); + console.log(`Total games: ${datasetStats.totalGames}`); + console.log(`Total moves: ${datasetStats.totalMoves}`); + console.log(`Average game length: ${datasetStats.averageGameLength.toFixed(1)} moves`); + console.log(`Average move time: ${datasetStats.averageMoveTime.toFixed(1)}ms`); + console.log(`Average score diff (W-B): ${datasetStats.averageScoreDiff.toFixed(2)}`); + + // Print per-board-shape statistics + if (datasetStats.boardShapeStats.length > 0) { + console.log(`\nBoard Shape Statistics:`); + for (const shapeStats of datasetStats.boardShapeStats) { + console.log(` [${shapeStats.boardShape}] ${shapeStats.gameCount} games, avg score: ${shapeStats.averageScoreDiff.toFixed(2)}, avg moves: ${shapeStats.averageMoveCount.toFixed(1)}`); + } + } + + console.log(`\nBlack wins: ${datasetStats.blackWins} (${((datasetStats.blackWins / datasetStats.totalGames) * 100).toFixed(1)}%)`); + console.log(`White wins: ${datasetStats.whiteWins} (${((datasetStats.whiteWins / datasetStats.totalGames) * 100).toFixed(1)}%)`); + console.log(`Resignations: ${datasetStats.resignations}`); + console.log(`Max moves reached: ${datasetStats.maxMovesReached}`); + console.log(`Total time: ${(datasetStats.generationTime / 1000 / 60).toFixed(1)} minutes`); + console.log(`\nOutput directory: ${config.outputDir}`); + console.log(`Statistics file: ${statsFilepath}`); + console.log("=".repeat(80)); +} + + +/** + * Main function + */ +async function main() { + try { + const config = parseArgs(); + await generateDataset(config); + } catch (error) { + console.error("Error:", error); + process.exit(1); + } +} + + +// Run main function +main(); diff --git a/trigo-web/tools/test5x1x1Game.ts b/trigo-web/tools/test5x1x1Game.ts new file mode 100644 index 0000000000000000000000000000000000000000..f7d03b02bdca4426c13f0024a4b2523763a09f98 --- /dev/null +++ b/trigo-web/tools/test5x1x1Game.ts @@ -0,0 +1,124 @@ +/** + * Test the 5x1x1 game to verify if it's really finished + * TGN: 1. y 0 2. b Pass 3. 0 + */ + +import { TrigoGame } from "../inc/trigo/game.js"; + +const StoneType = { + EMPTY: 0, + BLACK: 1, + WHITE: 2, +}; + + +function printBoard(game: TrigoGame) { + const shape = game.getShape(); + const board = game.getBoard(); + const stones = []; + for (let x = 0; x < shape.x; x++) { + const stone = board[x]?.[0]?.[0]; + if (stone === undefined) { + stones.push("?"); + } else { + stones.push(stone === StoneType.BLACK ? "B" : stone === StoneType.WHITE ? "W" : "."); + } + } + console.log(`Board: [${stones.join(", ")}]`); + console.log(` Shape: ${shape.x}×${shape.y}×${shape.z}`); +} + + +console.log("Testing 5x1x1 game from self-play:"); +console.log("TGN: 1. y 0 2. b Pass 3. 0"); +console.log(); + +const game = new TrigoGame({ x: 5, y: 1, z: 1 }); + +console.log("Initial board:"); +printBoard(game); +console.log(); + +// Move 1: Black y (y=3 in 5x1x1) +console.log("Move 1: Black plays y"); +game.drop({ x: 3, y: 0, z: 0 }); +printBoard(game); + +// Move 2: White 0 (x=2, center) +console.log("Move 2: White plays 0"); +game.drop({ x: 2, y: 0, z: 0 }); +printBoard(game); + +// Move 3: Black b (x=1) +console.log("Move 3: Black plays b"); +game.drop({ x: 1, y: 0, z: 0 }); +printBoard(game); + +// Move 4: White Pass +console.log("Move 4: White passes"); +game.pass(); +printBoard(game); + +// Move 5: Black 0 (x=2, center) - should capture White +console.log("Move 5: Black plays 0 (should capture White)"); +game.drop({ x: 2, y: 0, z: 0 }); +printBoard(game); +console.log(); + +// Check terminal status +const territory = game.getTerritory(); +console.log(`Territory: Black=${territory.black}, White=${territory.white}, Neutral=${territory.neutral}`); +console.log(); + +// Check if White can still capture +console.log("Checking if White can capture:"); +const whiteCanCapture = game.hasCapturingMove(StoneType.WHITE); +console.log(`White has capturing move? ${whiteCanCapture}`); + +if (whiteCanCapture) { + console.log("\n⚠️ WHITE CAN STILL CAPTURE!"); + console.log("Simulating White's capturing move:"); + + // Try all positions for White + const shape = game.getShape(); + const board = game.getBoard(); + for (let x = 0; x < shape.x; x++) { + if (board[x][0][0] === StoneType.EMPTY) { + // Create a test game to try this move + const testGame = TrigoGame.fromJSON(game.toJSON()); + try { + testGame.drop({ x, y: 0, z: 0 }); + const testBoard = testGame.getBoard(); + const testStones = []; + for (let i = 0; i < shape.x; i++) { + const stone = testBoard[i][0][0]; + testStones.push(stone === StoneType.BLACK ? "B" : stone === StoneType.WHITE ? "W" : "."); + } + console.log(` If White plays at x=${x}: [${testStones.join(", ")}]`); + + // Check if this captured any Black stones + const capturedInMove = testGame.getCurrentStep()?.captured || []; + if (capturedInMove.length > 0) { + console.log(` ✓ This move captures ${capturedInMove.length} Black stone(s)!`); + } + } catch (e) { + // Move not valid (suicide, etc.) + } + } + } +} + +console.log(); +console.log("Checking if Black can capture:"); +const blackCanCapture = game.hasCapturingMove(StoneType.BLACK); +console.log(`Black has capturing move? ${blackCanCapture}`); + +console.log(); +console.log("=".repeat(80)); +console.log("CONCLUSION:"); +if (!whiteCanCapture && !blackCanCapture) { + console.log("✓ Game is correctly finished - no captures possible"); +} else { + console.log("✗ Game should NOT be finished - captures still possible!"); +} +console.log("=".repeat(80)); diff --git a/trigo-web/tools/testAIAgent.ts b/trigo-web/tools/testAIAgent.ts new file mode 100644 index 0000000000000000000000000000000000000000..8941150ee88036028c8d2bd49a70de5fc3574f75 --- /dev/null +++ b/trigo-web/tools/testAIAgent.ts @@ -0,0 +1,313 @@ +/** + * Test script for Trigo AI Agent using onnxruntime-node + * + * This script tests the AI agent's ability to: + * 1. Load the ONNX evaluation model + * 2. Initialize the model inferencer + * 3. Score all valid moves using tree attention + * 4. Select and display the best move + * + * Usage: npx tsx tools/testAIAgent.ts + */ + +import * as ort from "onnxruntime-node"; +import * as path from "path"; +import { fileURLToPath } from "url"; +import { TrigoGame, StoneType } from "../inc/trigo/game"; +import { ModelInferencer } from "../inc/modelInferencer"; +import { TrigoTreeAgent } from "../inc/trigoTreeAgent"; +import type { Move } from "../inc/trigo/types"; +import { loadEnvConfig, getOnnxModelPaths, getAbsoluteModelPath, getOnnxSessionOptions } from "../inc/config"; + + +// ES module equivalent of __dirname +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Load environment variables +await loadEnvConfig(); + +// Configuration +const modelPaths = getOnnxModelPaths(); +const MODEL_PATH = getAbsoluteModelPath(modelPaths.evaluationModel); +const VOCAB_SIZE = 128; +const SEQ_LEN = 256; + + +/** + * Initialize the AI agent + */ +async function initializeAgent(): Promise { + console.log("=".repeat(80)); + console.log("Initializing AI Agent..."); + console.log("=".repeat(80)); + console.log(`Model Path: ${MODEL_PATH}`); + console.log(`Vocab Size: ${VOCAB_SIZE}`); + console.log(`Sequence Length: ${SEQ_LEN}`); + console.log(); + + // Load ONNX model + console.log("Loading ONNX model..."); + const sessionOptions = getOnnxSessionOptions(); + const session = await ort.InferenceSession.create(MODEL_PATH, sessionOptions); + console.log("✓ Model loaded successfully"); + + // Create inferencer + console.log("Creating model inferencer..."); + const inferencer = new ModelInferencer(ort.Tensor as any, { + vocabSize: VOCAB_SIZE, + seqLen: SEQ_LEN, + modelPath: MODEL_PATH + }); + inferencer.setSession(session as any); + console.log("✓ Inferencer created"); + + // Create agent + console.log("Creating Trigo tree agent..."); + const agent = new TrigoTreeAgent(inferencer); + console.log("✓ Agent initialized"); + console.log(); + + return agent; +} + + +/** + * Test AI move generation on a fresh game + */ +async function testFreshGame(agent: TrigoTreeAgent): Promise { + console.log("=".repeat(80)); + console.log("Test 1: Fresh Game (First Move)"); + console.log("=".repeat(80)); + + // Create a new game + const game = new TrigoGame({ x: 5, y: 5, z: 5 }, {}); + console.log("Game created: 5×5×5 board"); + console.log(`Current player: ${game.getCurrentPlayer() === StoneType.BLACK ? "Black" : "White"}`); + console.log(`Valid move positions: ${game.validMovePositions().length}`); + console.log(); + + // Get all valid moves + const currentPlayer = game.getCurrentPlayer() === StoneType.BLACK ? "black" : "white"; + const validPositions = game.validMovePositions(); + const moves: Move[] = validPositions.map((pos) => ({ + x: pos.x, + y: pos.y, + z: pos.z, + player: currentPlayer + })); + moves.push({ player: currentPlayer, isPass: true }); // Add pass + + console.log(`Scoring ${moves.length} moves (${moves.length - 1} positions + pass)...`); + const startTime = Date.now(); + + // Score all moves + const scoredMoves = await agent.scoreMoves(game, moves); + + const endTime = Date.now(); + const timeMs = endTime - startTime; + + console.log(`✓ Scored ${scoredMoves.length} moves in ${timeMs}ms`); + console.log(` Average: ${(timeMs / scoredMoves.length).toFixed(2)}ms per move`); + console.log(); + + // Sort by score + scoredMoves.sort((a, b) => b.score - a.score); + + // Compute softmax probabilities + const maxScore = Math.max(...scoredMoves.map((m) => m.score)); + const expScores = scoredMoves.map((m) => Math.exp(m.score - maxScore)); + const sumExp = expScores.reduce((sum, exp) => sum + exp, 0); + const probabilities = expScores.map((exp) => exp / sumExp); + + // Display top 10 moves + console.log("Top 10 Moves:"); + console.log("-".repeat(80)); + console.log("Rank | Notation | Position | Log Prob | Probability"); + console.log("-".repeat(80)); + + for (let i = 0; i < Math.min(10, scoredMoves.length); i++) { + const move = scoredMoves[i]; + const prob = probabilities[i]; + const position = move.move.isPass + ? "Pass " + : `(${move.move.x}, ${move.move.y}, ${move.move.z}) `.slice(0, 15); + + console.log( + `${(i + 1).toString().padStart(4)} | ` + + `${move.notation.padEnd(8)} | ` + + `${position} | ` + + `${move.score.toFixed(4).padStart(9)} | ` + + `${(prob * 100).toFixed(4)}%` + ); + } + console.log(); +} + + +/** + * Test AI move generation after several moves + */ +async function testMidGame(agent: TrigoTreeAgent): Promise { + console.log("=".repeat(80)); + console.log("Test 2: Mid Game (After Several Moves)"); + console.log("=".repeat(80)); + + // Create a new game and make some moves + const game = new TrigoGame({ x: 5, y: 5, z: 5 }, {}); + + // Make some test moves + const testMoves = [ + { x: 2, y: 2, z: 2 }, // Center + { x: 1, y: 1, z: 1 }, // Near corner + { x: 3, y: 2, z: 2 }, // Adjacent to center + { x: 1, y: 2, z: 1 }, // Another move + ]; + + console.log("Playing test moves:"); + for (const pos of testMoves) { + const success = game.drop(pos); + const player = game.getCurrentPlayer() === StoneType.BLACK ? "White" : "Black"; + console.log(` ${player} plays (${pos.x}, ${pos.y}, ${pos.z}): ${success ? "✓" : "✗"}`); + } + console.log(); + + // Display current TGN + console.log("Current TGN:"); + console.log("-".repeat(80)); + console.log(game.toTGN().trim()); + console.log("-".repeat(80)); + console.log(); + + // Get current state + console.log(`Current player: ${game.getCurrentPlayer() === StoneType.BLACK ? "Black" : "White"}`); + console.log(`Move count: ${game.stepHistory.length}`); + console.log(`Valid move positions: ${game.validMovePositions().length}`); + console.log(); + + // Select best move + console.log("Selecting best move..."); + const startTime = Date.now(); + const bestMove = await agent.selectBestMove(game); + const endTime = Date.now(); + + if (bestMove) { + const moveStr = bestMove.isPass + ? "Pass" + : `(${bestMove.x}, ${bestMove.y}, ${bestMove.z})`; + console.log(`✓ Best move: ${moveStr} (selected in ${endTime - startTime}ms)`); + } else { + console.log("✗ No valid move found"); + } + console.log(); +} + + +/** + * Test tree structure visualization + */ +async function testTreeStructure(agent: TrigoTreeAgent): Promise { + console.log("=".repeat(80)); + console.log("Test 3: Tree Structure Visualization"); + console.log("=".repeat(80)); + + // Create a simple game + const game = new TrigoGame({ x: 5, y: 5, z: 5 }, {}); + + // Get limited moves for clearer visualization + const currentPlayer = game.getCurrentPlayer() === StoneType.BLACK ? "black" : "white"; + const validPositions = game.validMovePositions().slice(0, 5); // Only first 5 positions + const moves: Move[] = validPositions.map((pos) => ({ + x: pos.x, + y: pos.y, + z: pos.z, + player: currentPlayer + })); + moves.push({ player: currentPlayer, isPass: true }); // Add pass + + console.log(`Analyzing tree structure for ${moves.length} moves...`); + console.log(); + + // Get tree structure + const treeStructure = agent.getTreeStructure(game, moves); + + console.log(`Tree nodes: ${treeStructure.evaluatedIds.length}`); + console.log(`Move count: ${treeStructure.moveData.length}`); + console.log(); + + // Display token sequence + console.log("Evaluated Token Sequence:"); + console.log("-".repeat(80)); + const tokens = treeStructure.evaluatedIds.map((id) => String.fromCharCode(id)).join(""); + console.log(`Tokens: ${tokens}`); + console.log(`Token IDs: [${treeStructure.evaluatedIds.join(", ")}]`); + console.log(); + + // Display move details + console.log("Move Details:"); + console.log("-".repeat(80)); + console.log("Notation | Leaf Pos | Parent"); + console.log("-".repeat(80)); + for (const data of treeStructure.moveData) { + // Find parent from parent array + const parentPos = treeStructure.parent[data.leafPos]; + const parentStr = parentPos === null ? "null" : parentPos.toString(); + + console.log( + `${data.notation.padEnd(8)} | ` + + `${data.leafPos.toString().padStart(8)} | ` + + `${parentStr.padStart(10)}` + ); + } + console.log(); + + // Display attention mask (simplified) + console.log("Attention Mask (1 = can attend, 0 = cannot attend):"); + console.log("-".repeat(80)); + const size = treeStructure.evaluatedIds.length; + + // Header + process.stdout.write(" "); + for (let col = 0; col < size; col++) { + process.stdout.write(col.toString().padStart(2)); + } + console.log(); + + // Rows + for (let row = 0; row < size; row++) { + process.stdout.write(row.toString().padStart(3) + " "); + for (let col = 0; col < size; col++) { + const value = treeStructure.mask[row * size + col]; + process.stdout.write(value.toString().padStart(2)); + } + console.log(); + } + console.log(); +} + + +/** + * Main test function + */ +async function main() { + try { + // Initialize agent + const agent = await initializeAgent(); + + // Run tests + await testFreshGame(agent); + await testMidGame(agent); + await testTreeStructure(agent); + + console.log("=".repeat(80)); + console.log("All tests completed successfully!"); + console.log("=".repeat(80)); + } catch (error) { + console.error("Error:", error); + process.exit(1); + } +} + + +// Run main function +main(); diff --git a/trigo-web/tools/testMCTSValueSign.ts b/trigo-web/tools/testMCTSValueSign.ts new file mode 100644 index 0000000000000000000000000000000000000000..8072033616f2388dcaf5917329361c1aba091e6f --- /dev/null +++ b/trigo-web/tools/testMCTSValueSign.ts @@ -0,0 +1,144 @@ +/** + * Test MCTS value sign correction + * + * This script verifies that the MCTS agent correctly handles value signs + * from both Black and White perspectives. + */ + +import { TrigoGame } from "../inc/trigo/game"; +import { MCTSAgent } from "../inc/mctsAgent"; +import { TrigoTreeAgent } from "../inc/trigoTreeAgent"; +import { TrigoEvaluationAgent } from "../inc/trigoEvaluationAgent"; +import { ModelInferencer } from "../inc/modelInferencer"; +import { loadEnvConfig, getOnnxSessionOptions } from "../inc/config"; +import * as ort from "onnxruntime-node"; + + +async function main() { + // Load environment config + await loadEnvConfig(); + + console.log("=== MCTS Value Sign Test ===\n"); + + // Initialize agents + const policyModelPath = process.env.ONNX_TREE_MODEL || "./models/policy_model.onnx"; + const valueModelPath = process.env.ONNX_EVALUATION_MODEL || "./models/value_model.onnx"; + const vocabSize = parseInt(process.env.VOCAB_SIZE || "342", 10); + const seqLen = parseInt(process.env.SEQ_LEN || "256", 10); + + console.log("Loading models..."); + console.log(`Policy: ${policyModelPath}`); + console.log(`Value: ${valueModelPath}`); + console.log(`Vocab Size: ${vocabSize}`); + console.log(`Seq Length: ${seqLen}\n`); + + // Load sessions + const sessionOptions = getOnnxSessionOptions(); + const treeSession = await ort.InferenceSession.create(policyModelPath, sessionOptions); + const evalSession = await ort.InferenceSession.create(valueModelPath, sessionOptions); + + // Create inferencers + const treeInferencer = new ModelInferencer(ort.Tensor as any, { + vocabSize, + seqLen, + modelPath: policyModelPath + }); + treeInferencer.setSession(treeSession as any); + + const evalInferencer = new ModelInferencer(ort.Tensor as any, { + vocabSize, + seqLen, + modelPath: valueModelPath + }); + evalInferencer.setSession(evalSession as any); + + // Create agents + const treeAgent = new TrigoTreeAgent(treeInferencer); + const evaluationAgent = new TrigoEvaluationAgent(evalInferencer); + + // Create MCTS agent with fewer simulations for quick test + const mctsAgent = new MCTSAgent(treeAgent, evaluationAgent, { + numSimulations: 100, + cPuct: 1.0, + temperature: 1.0 + }); + + // Test 1: Black's first move + console.log("--- Test 1: Black's First Move ---"); + const game1 = new TrigoGame({ x: 5, y: 5, z: 5 }); + console.log(`Current player: Black (${game1.getCurrentPlayer()})`); + + const eval1 = await evaluationAgent.evaluatePosition(game1); + console.log(`Evaluation value (Black perspective): ${eval1.value.toFixed(4)}`); + console.log(`Interpretation: ${eval1.interpretation}\n`); + + const result1 = await mctsAgent.selectMove(game1); + console.log(`Root value (Black perspective): ${result1.rootValue.toFixed(4)}`); + console.log(`Selected move: (${result1.move.x}, ${result1.move.y}, ${result1.move.z})\n`); + + + // Test 2: White's first move (after Black plays) + console.log("--- Test 2: White's Response ---"); + const game2 = new TrigoGame({ x: 5, y: 5, z: 5 }); + game2.dropStone({ x: 2, y: 2, z: 2 }); // Black plays center + console.log(`Current player: White (${game2.getCurrentPlayer()})`); + + const eval2 = await evaluationAgent.evaluatePosition(game2); + console.log(`Evaluation value (Black perspective): ${eval2.value.toFixed(4)}`); + console.log(`Interpretation: ${eval2.interpretation}`); + console.log(`From White's perspective: ${(-eval2.value).toFixed(4)}\n`); + + const result2 = await mctsAgent.selectMove(game2); + console.log(`Root value (White perspective): ${result2.rootValue.toFixed(4)}`); + console.log(`Selected move: (${result2.move.x}, ${result2.move.y}, ${result2.move.z})\n`); + + + // Test 3: Play a few moves and check consistency + console.log("--- Test 3: Short Game ---"); + const game3 = new TrigoGame({ x: 5, y: 5, z: 5 }); + + for (let i = 0; i < 6; i++) { + const player = game3.getCurrentPlayer() === 1 ? "Black" : "White"; + console.log(`\nMove ${i + 1} - ${player}'s turn`); + + const evalBefore = await evaluationAgent.evaluatePosition(game3); + console.log(` Position value (Black perspective): ${evalBefore.value.toFixed(4)}`); + + const result = await mctsAgent.selectMove(game3); + console.log(` Root value (${player} perspective): ${result.rootValue.toFixed(4)}`); + + // Check sign consistency + if (player === "Black") { + // For Black, root value should match eval value + const diff = Math.abs(result.rootValue - evalBefore.value); + if (diff > 0.1) { + console.log(` ⚠️ Warning: Value mismatch! Expected ~${evalBefore.value.toFixed(4)}, got ${result.rootValue.toFixed(4)}`); + } + } else { + // For White, root value should be negated eval value + const expectedWhiteValue = -evalBefore.value; + const diff = Math.abs(result.rootValue - expectedWhiteValue); + if (diff > 0.1) { + console.log(` ⚠️ Warning: Value mismatch! Expected ~${expectedWhiteValue.toFixed(4)}, got ${result.rootValue.toFixed(4)}`); + } + } + + if (!result.move.isPass) { + game3.dropStone({ + x: result.move.x!, + y: result.move.y!, + z: result.move.z! + }); + console.log(` Played: (${result.move.x}, ${result.move.y}, ${result.move.z})`); + } else { + game3.pass(); + console.log(` Played: PASS`); + } + } + + console.log("\n=== Test Complete ==="); + console.log("If no warnings appeared, value signs are handled correctly!"); +} + + +main().catch(console.error); diff --git a/trigo-web/tools/testTerminal.ts b/trigo-web/tools/testTerminal.ts new file mode 100644 index 0000000000000000000000000000000000000000..bb1399b8d3d37c87f8b4d5fb7fa803085a70f96d --- /dev/null +++ b/trigo-web/tools/testTerminal.ts @@ -0,0 +1,120 @@ +import { TrigoGame } from "../inc/trigo/game"; + +// Test hasCapturingMove() function + +console.log("=== Test: hasCapturingMove() on 7x1x1 ===\n"); + +// 7x1x1: a(0), b(1), c(2), 0(3), x(4), y(5), z(6) + +const game = new TrigoGame({ x: 7, y: 1, z: 1 }); +game.startGame(); + +const labels = ["a", "b", "c", "0", "x", "y", "z"]; + +function showState(msg: string) { + console.log(`\n--- ${msg} ---`); + const board = game.getBoard(); + const boardStr = labels.map((l, i) => { + const s = board[i][0][0]; + return `${l}:${s === 0 ? "." : s === 1 ? "B" : "W"}`; + }).join(" "); + console.log(`Board: ${boardStr}`); + console.log(`Current player: ${game.getCurrentPlayer() === 1 ? "Black" : "White"}`); + console.log(`hasCapturingMove(): ${game.hasCapturingMove()}`); + const territory = game.getTerritory(); + console.log(`Territory: B=${territory.black} W=${territory.white} N=${territory.neutral}`); +} + +// Initial state +showState("Initial (empty board)"); + +// 1. Black plays 0 (center) +game.drop({ x: 3, y: 0, z: 0 }); +console.log("\n1. Black: 0"); +showState("After Black 0"); + +// 2. White plays z (corner) +game.drop({ x: 6, y: 0, z: 0 }); +console.log("\n2. White: z"); +showState("After White z"); + +// 3. Black plays y (threatens z) +game.drop({ x: 5, y: 0, z: 0 }); +console.log("\n3. Black: y (captures White z!)"); +showState("After Black y - White z captured"); + +// Let's create a different scenario where capturing is possible but not yet done +console.log("\n\n" + "=".repeat(60)); +console.log("=== Test 2: Position with capturing move available ==="); +console.log("=".repeat(60)); + +const game2 = new TrigoGame({ x: 7, y: 1, z: 1 }); +game2.startGame(); + +// Build: 1. 0 z 2. b Pass +// After this, Black can play 'y' to capture White's z (z has 1 liberty at y) +const moves2 = [ + { pos: { x: 3, y: 0, z: 0 }, name: "0" }, // Black: center + { pos: { x: 6, y: 0, z: 0 }, name: "z" }, // White: corner z (has 1 liberty at y) + { pos: { x: 1, y: 0, z: 0 }, name: "b" }, // Black: b (doesn't threaten z) + { pos: null, name: "Pass" }, // White: Pass +]; + +for (const move of moves2) { + if (move.pos) { + game2.drop(move.pos); + } else { + game2.pass(); + } +} + +const board2 = game2.getBoard(); +const boardStr2 = labels.map((l, i) => { + const s = board2[i][0][0]; + return `${l}:${s === 0 ? "." : s === 1 ? "B" : "W"}`; +}).join(" "); +console.log(`\nBoard: ${boardStr2}`); +console.log(`Current player: ${game2.getCurrentPlayer() === 1 ? "Black" : "White"}`); +console.log("Black can play 'y' to capture White's z"); + +// Now check both players +console.log(`\nhasCapturingMove(Black): ${game2.hasCapturingMove(1)}`); +console.log(`hasCapturingMove(White): ${game2.hasCapturingMove(2)}`); + +console.log("\n" + "=".repeat(60)); +console.log("=== Test 3: Position with NO capturing moves ==="); +console.log("=".repeat(60)); + +const game3 = new TrigoGame({ x: 5, y: 1, z: 1 }); +game3.startGame(); + +// 5x1x1: a(0), b(1), 0(2), x(3), y(4) +// Build a position where all stones are safe +// 1. 0 (Black center) 2. Pass 3. b 4. Pass 5. x +const moves3 = [ + { pos: { x: 2, y: 0, z: 0 }, name: "0" }, + { pos: null, name: "Pass" }, + { pos: { x: 1, y: 0, z: 0 }, name: "b" }, + { pos: null, name: "Pass" }, + { pos: { x: 3, y: 0, z: 0 }, name: "x" }, +]; + +for (const move of moves3) { + if (move.pos) { + game3.drop(move.pos); + } else { + game3.pass(); + } +} + +const labels5 = ["a", "b", "0", "x", "y"]; +const board3 = game3.getBoard(); +const boardStr3 = labels5.map((l, i) => { + const s = board3[i][0][0]; + return `${l}:${s === 0 ? "." : s === 1 ? "B" : "W"}`; +}).join(" "); +console.log(`\nBoard: ${boardStr3}`); +console.log(`Current player: ${game3.getCurrentPlayer() === 1 ? "Black" : "White"}`); +console.log(`hasCapturingMove(): ${game3.hasCapturingMove()}`); +const territory3 = game3.getTerritory(); +console.log(`Territory: B=${territory3.black} W=${territory3.white} N=${territory3.neutral}`); diff --git a/trigo-web/tools/testTerminal2x3.ts b/trigo-web/tools/testTerminal2x3.ts new file mode 100644 index 0000000000000000000000000000000000000000..8ba2a5dcf7fb9b2654424b652b34304e5e1d007a --- /dev/null +++ b/trigo-web/tools/testTerminal2x3.ts @@ -0,0 +1,85 @@ +import { TrigoGame } from "../inc/trigo/game"; + +// Test case: 2x3 board with moves aa zz z0 a0 +// Expected: NOT terminal + +console.log("=== Test: 2x3 board - aa zz z0 a0 ===\n"); + +// 2x3x1 board layout: +// y=2: az zz +// y=1: a0 z0 +// y=0: aa za +// x=0 x=1 + +// For 2x3 board notation: +// x: a(0), z(1) +// y: a(0), 0(1), z(2) + +const game = new TrigoGame({ x: 2, y: 3, z: 1 }); +game.startGame(); + +console.log("Board shape: 2x3x1"); +console.log("Position notation:"); +console.log(" y=2: az(0,2) zz(1,2)"); +console.log(" y=1: a0(0,1) z0(1,1)"); +console.log(" y=0: aa(0,0) za(1,0)"); +console.log(); + +// Moves: aa zz z0 a0 +const moves = [ + { pos: { x: 0, y: 0, z: 0 }, name: "aa" }, // Black + { pos: { x: 1, y: 2, z: 0 }, name: "zz" }, // White + { pos: { x: 1, y: 1, z: 0 }, name: "z0" }, // Black + { pos: { x: 0, y: 1, z: 0 }, name: "a0" }, // White +]; + +for (let i = 0; i < moves.length; i++) { + const move = moves[i]; + const player = i % 2 === 0 ? "Black" : "White"; + const success = game.drop(move.pos); + console.log(`${i + 1}. ${player}: ${move.name} (${move.pos.x},${move.pos.y}) - ${success ? "OK" : "FAILED"}`); +} + +// Show board state +console.log("\nBoard state:"); +const board = game.getBoard(); +for (let y = 2; y >= 0; y--) { + let row = `y=${y}: `; + for (let x = 0; x < 2; x++) { + const stone = board[x][y][0]; + const mark = stone === 0 ? "." : (stone === 1 ? "B" : "W"); + row += mark + " "; + } + console.log(row); +} + +// Check terminal conditions +const territory = game.getTerritory(); +console.log("\nTerritory:", { black: territory.black, white: territory.white, neutral: territory.neutral }); + +const hasCapture = game.hasCapturingMove(); +console.log("hasCapturingMove():", hasCapture); + +const validMoves = game.validMovePositions(); +console.log("validMovePositions():", validMoves.length); + +// Count coverage +let stoneCount = 0; +for (let x = 0; x < 2; x++) { + for (let y = 0; y < 3; y++) { + if (board[x][y][0] !== 0) stoneCount++; + } +} +const coverage = stoneCount / 6; +console.log("Coverage:", (coverage * 100).toFixed(1) + "%"); + +console.log("\n" + "=".repeat(50)); +console.log("Terminal check:"); +console.log(" - neutral == 0?", territory.neutral === 0); +console.log(" - coverage > 50%?", coverage > 0.5); +console.log(" - hasCapturingMove?", hasCapture); + +const shouldBeTerminal = territory.neutral === 0 && coverage > 0.5 && !hasCapture; +console.log("\nWith current logic, isTerminal:", shouldBeTerminal); +console.log("Expected: false (NOT terminal)"); +console.log("Test", shouldBeTerminal === false ? "PASSED ✓" : "FAILED ✗"); diff --git a/trigo-web/tools/testTerminalBug.ts b/trigo-web/tools/testTerminalBug.ts new file mode 100644 index 0000000000000000000000000000000000000000..678a0b3c5085c164a7a7a052173720bdb27d690a --- /dev/null +++ b/trigo-web/tools/testTerminalBug.ts @@ -0,0 +1,107 @@ +/** + * Test case to verify incorrect terminal detection + * + * Scenario: 5x1x1 board + * 1. a y - Black a(0), White y(3) + * 2. b z - Black b(1), White z(4) + * 3. 0 - Black 0(2) captures White {y, z} + * + * After move 3: + * Board: [Black(a), Black(b), Black(0), Empty(y), Empty(z)] + * + * Problem: System declares game finished with "Black: 5, White: 0" + * Reality: White can still play at y or z and potentially capture Black stones + */ + +import { TrigoGame } from "../inc/trigo/game.js"; + +function testTerminalDetection() { + console.log("================================================================================"); + console.log("Testing Terminal Detection Bug in 5x1x1 Board"); + console.log("================================================================================\n"); + + const game = new TrigoGame({ x: 5, y: 1, z: 1 }); + game.startGame(); + + console.log("Initial board (5x1x1):"); + console.log("Positions: a(0) - b(1) - 0(2) - y(3) - z(4)\n"); + + // Move 1: Black a, White y + console.log("Move 1: Black plays a(0)"); + game.drop({ x: 0, y: 0, z: 0 }); + console.log("Board:", game.getBoard().flat().flat()); + console.log("Territory:", game.getTerritory()); + + console.log("\nMove 1b: White plays y(3)"); + game.drop({ x: 3, y: 0, z: 0 }); + console.log("Board:", game.getBoard().flat().flat()); + console.log("Territory:", game.getTerritory()); + + // Move 2: Black b, White z + console.log("\nMove 2: Black plays b(1)"); + game.drop({ x: 1, y: 0, z: 0 }); + console.log("Board:", game.getBoard().flat().flat()); + console.log("Territory:", game.getTerritory()); + + console.log("\nMove 2b: White plays z(4)"); + game.drop({ x: 4, y: 0, z: 0 }); + console.log("Board:", game.getBoard().flat().flat()); + console.log("Territory:", game.getTerritory()); + + // Move 3: Black 0 - should capture White {y, z} + console.log("\nMove 3: Black plays 0(2) - should capture White stones"); + game.drop({ x: 2, y: 0, z: 0 }); + console.log("Board:", game.getBoard().flat().flat()); + console.log("Territory:", game.getTerritory()); + console.log("Game status:", game.getGameStatus()); + + // Check valid moves for White + console.log("\n" + "=".repeat(80)); + console.log("Analysis after Move 3:"); + console.log("=".repeat(80)); + + const validMoves = game.validMovePositions(); + console.log("\nValid move positions for White:", validMoves); + console.log("Number of valid moves:", validMoves.length); + + // Check if White can capture + const whiteCanCapture = game.hasCapturingMove(2); + console.log("\nWhite has capturing move available?", whiteCanCapture); + + // Try White playing at y(3) + console.log("\n" + "=".repeat(80)); + console.log("Hypothetical: What if White plays at y(3)?"); + console.log("=".repeat(80)); + + const testGame = game.clone(); + try { + testGame.drop({ x: 3, y: 0, z: 0 }); + console.log("Board after White y(3):", testGame.getBoard().flat().flat()); + console.log("Territory:", testGame.getTerritory()); + + // Check if any Black stones were captured + const boardAfter = testGame.getBoard().flat().flat(); + const blackStones = boardAfter.filter(s => s === 1).length; + const whiteStones = boardAfter.filter(s => s === 2).length; + console.log(`Stones on board: Black=${blackStones}, White=${whiteStones}`); + + if (blackStones < 3) { + console.log("\n⚠️ BLACK STONES WERE CAPTURED!"); + console.log("This proves the game was NOT terminal after move 3!"); + } + } catch (error: any) { + console.log("Error:", error.message); + } + + // Conclusion + console.log("\n" + "=".repeat(80)); + console.log("CONCLUSION:"); + console.log("=".repeat(80)); + console.log("The current terminal detection logic is INCORRECT."); + console.log("It only checks hasBlack && hasWhite, which fails when one player"); + console.log("has all their stones captured."); + console.log("\nThe game should continue until no more captures are possible,"); + console.log("not just when neutral territory is zero."); +} + +testTerminalDetection(); diff --git a/trigo-web/tools/testTerminalPropagation.ts b/trigo-web/tools/testTerminalPropagation.ts new file mode 100644 index 0000000000000000000000000000000000000000..ba4e58a10597ff926d12214f7886ee6374d92508 --- /dev/null +++ b/trigo-web/tools/testTerminalPropagation.ts @@ -0,0 +1,132 @@ +/** + * Test terminal propagation with debug output + * Runs a single 5x1x1 game with MCTS and debug mode enabled + */ + +import * as ort from "onnxruntime-node"; +import { TrigoGame } from "../inc/trigo/game.js"; +import { TrigoTreeAgent } from "../inc/trigoTreeAgent.js"; +import { TrigoEvaluationAgent } from "../inc/trigoEvaluationAgent.js"; +import { MCTSAgent } from "../inc/mctsAgent.js"; +import { ModelInferencer } from "../inc/modelInferencer.js"; +import { encodeAb0yz } from "../inc/trigo/ab0yz.js"; + + +async function main() { + console.log("================================================================================"); + console.log("MCTS Terminal Propagation Test - 5x1x1 Board"); + console.log("================================================================================\n"); + + // Model paths + const modelDir = "/home/camus/work/trigo/trigo-web/public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500"; + const treeModelPath = `${modelDir}/GPT2CausalLM_ep0019_tree.onnx`; + const evalModelPath = `${modelDir}/GPT2CausalLM_ep0019_evaluation.onnx`; + + console.log("Loading models..."); + console.log(` Tree: ${treeModelPath}`); + console.log(` Eval: ${evalModelPath}\n`); + + // Create ONNX sessions + const sessionOptions: ort.InferenceSession.SessionOptions = { + executionProviders: ["cpu"] + }; + + const treeSession = await ort.InferenceSession.create(treeModelPath, sessionOptions); + const evalSession = await ort.InferenceSession.create(evalModelPath, sessionOptions); + + // Create model inferencers + const treeInferencer = new ModelInferencer(ort.Tensor as any, { + vocabSize: 128, + seqLen: 256 + }); + treeInferencer.setSession(treeSession as any); + + const evalInferencer = new ModelInferencer(ort.Tensor as any, { + vocabSize: 128, + seqLen: 256 + }); + evalInferencer.setSession(evalSession as any); + + // Create agents + const treeAgent = new TrigoTreeAgent(treeInferencer); + const evaluationAgent = new TrigoEvaluationAgent(evalInferencer); + + // Create MCTS agent with DEBUG MODE ENABLED + const mctsAgent = new MCTSAgent(treeAgent, evaluationAgent, { + numSimulations: 200, + cPuct: 1.0, + temperature: 1.0, + dirichletAlpha: 0.03, + dirichletEpsilon: 0.25 + }); + + // ENABLE DEBUG MODE + mctsAgent.debugMode = true; + + console.log("✓ MCTS Agent created with debug mode enabled\n"); + + // Create game + const game = new TrigoGame({ x: 5, y: 1, z: 1 }); + game.startGame(); + + console.log("================================================================================"); + console.log("Starting Self-Play Game"); + console.log("================================================================================\n"); + + let moveNumber = 0; + let maxMoves = 20; + + while (game.getGameStatus() === "playing" && moveNumber < maxMoves) { + const currentPlayer = game.getCurrentPlayer() === 1 ? "Black" : "White"; + console.log(`\n[Move ${moveNumber + 1}] ${currentPlayer} to move`); + console.log("Current board:", game.getBoard().flat().flat()); + + // Select move with MCTS + const startTime = Date.now(); + const result = await mctsAgent.selectMove(game, moveNumber); + const duration = Date.now() - startTime; + + // Log move selection + let moveStr: string; + if (result.move.isPass) { + moveStr = "Pass"; + } else { + moveStr = encodeAb0yz([result.move.x!, result.move.y!, result.move.z!], [5, 1, 1]); + } + + console.log(`\nSelected: ${moveStr}`); + console.log(`Root value: ${result.rootValue.toFixed(4)}`); + console.log(`Time: ${duration}ms`); + console.log(`Visit counts: ${Array.from(result.visitCounts.values()).reduce((a, b) => a + b, 0)}`); + + // Apply move + if (result.move.isPass) { + game.pass(); + } else { + game.drop({ x: result.move.x!, y: result.move.y!, z: result.move.z! }); + } + + moveNumber++; + + // Check terminal status + if (game.getGameStatus() === "finished") { + console.log("\n" + "=".repeat(80)); + console.log("Game finished!"); + const territory = game.getTerritory(); + console.log(`Final territory: Black=${territory.black}, White=${territory.white}, Neutral=${territory.neutral}`); + console.log(`Score diff (W-B): ${territory.white - territory.black}`); + break; + } + } + + // Print game notation + console.log("\n" + "=".repeat(80)); + console.log("Game TGN:"); + console.log("=".repeat(80)); + console.log(game.toTGN()); + + console.log("Test complete!"); +} + + +main().catch(console.error); diff --git a/trigo-web/tools/test_ts_values.ts b/trigo-web/tools/test_ts_values.ts new file mode 100644 index 0000000000000000000000000000000000000000..b814b2e409599c9fc034845d008b18594af4d067 --- /dev/null +++ b/trigo-web/tools/test_ts_values.ts @@ -0,0 +1,42 @@ +import * as ort from "onnxruntime-web"; +import { ModelInferencer } from "./inc/modelInferencer"; + +async function main() { + const modelPath = "/home/camus/work/trigo/trigo-web/public/onnx/20251204-trigo-value-gpt2-l6-h64-251125-lr500/GPT2CausalLM_ep0019_evaluation.onnx"; + + const inferencer = new ModelInferencer(ort.Tensor, { seqLen: 256 }); + const session = await ort.InferenceSession.create(modelPath); + inferencer.setSession(session); + + // Helper to tokenize TGN + const tokenize = (tgn: string): number[] => { + const START = 1; + const END = 2; + const tokens: number[] = [START]; + for (let i = 0; i < tgn.length; i++) { + tokens.push(tgn.charCodeAt(i)); + } + tokens.push(END); + // Pad to 256 + while (tokens.length < 256) { + tokens.push(0); + } + return tokens; + }; + + const testPositions = [ + { tgn: "[Board 5x5]\n\n", desc: "Empty board" }, + { tgn: "[Board 5x5]\n\n1. Pass ", desc: "After Black Pass" }, + { tgn: "[Board 5x5]\n\n1. aa ", desc: "After Black aa" }, + { tgn: "[Board 5x5]\n\n1. Pass Pass\n", desc: "Both pass" }, + { tgn: "[Board 5x5]\n\n1. aa zz\n2. ", desc: "After aa zz" }, + ]; + + for (const pos of testPositions) { + const tokens = tokenize(pos.tgn); + const value = await inferencer.runValuePrediction(tokens); + console.log(pos.desc + ": value = " + value); + } +} + +main().catch(console.error); diff --git a/trigo-web/tools/tgnToMoveSequence.ts b/trigo-web/tools/tgnToMoveSequence.ts new file mode 100644 index 0000000000000000000000000000000000000000..0241818b693be4ebcfe82a1fe770f1ecdc57444b --- /dev/null +++ b/trigo-web/tools/tgnToMoveSequence.ts @@ -0,0 +1,138 @@ +#!/usr/bin/env node +/** + * Convert TGN files to move sequences (JSON format) + * + * Output format for C++ consumption: + * { + * "boardShape": { "x": 5, "y": 5, "z": 1 }, + * "moves": [ + * { "x": 1, "y": 2, "z": 0, "player": 1 }, // BLACK = 1 + * { "x": 2, "y": 2, "z": 0, "player": 2 }, // WHITE = 2 + * null, // pass move + * ... + * ] + * } + */ + +import { TrigoGame, StoneType, StepType } from "@inc/trigo/game.js"; +import { calculateTerritory } from "@inc/trigo/gameUtils.js"; +import { initializeParsers } from "@inc/trigo/parserInit.js"; +import * as fs from "fs"; +import * as path from "path"; + + +interface MoveSequenceOutput { + tgnFile: string; + boardShape: { x: number; y: number; z: number }; + moves: Array<{ x: number; y: number; z: number; player: number } | null>; + finalBoard: number[][][]; + territory: { + black: number; + white: number; + neutral: number; + }; + error?: string; +} + + +function convertTgnToMoveSequence(tgnFilePath: string): MoveSequenceOutput { + try { + const tgnContent = fs.readFileSync(tgnFilePath, "utf-8"); + + // Parse TGN + const game = TrigoGame.fromTGN(tgnContent); + + const shape = game.getShape(); + const history = game.getHistory(); + + // Convert steps to simple move format + const moveSequence = history + .filter(step => step.type === StepType.DROP || step.type === StepType.PASS) + .map(step => { + if (step.type === StepType.PASS) { + return null; // Pass move + } + + if (step.type === StepType.DROP && step.position) { + return { + x: step.position.x, + y: step.position.y, + z: step.position.z, + player: step.player === StoneType.BLACK ? 1 : 2 // BLACK = 1, WHITE = 2 + }; + } + + return null; + }); + + // Get final board state and territory + const board = game.getBoard(); + const territory = calculateTerritory(board, shape); + + return { + tgnFile: path.basename(tgnFilePath), + boardShape: shape, + moves: moveSequence, + finalBoard: board, + territory: { + black: territory.black, + white: territory.white, + neutral: territory.neutral + } + }; + } catch (error) { + return { + tgnFile: path.basename(tgnFilePath), + boardShape: { x: 0, y: 0, z: 0 }, + moves: [], + finalBoard: [], + territory: { black: 0, white: 0, neutral: 0 }, + error: error instanceof Error ? error.message : String(error) + }; + } +} + + +function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: tgnToMoveSequence.ts [ ...]"); + console.error(""); + console.error("Converts TGN files to move sequences for C++ validation."); + console.error("Outputs JSON to stdout."); + process.exit(1); + } + + // Initialize parsers first + initializeParsers().then(() => { + const results: MoveSequenceOutput[] = []; + + for (const tgnFile of args) { + if (!fs.existsSync(tgnFile)) { + console.error(`File not found: ${tgnFile}`); + results.push({ + tgnFile: path.basename(tgnFile), + boardShape: { x: 0, y: 0, z: 0 }, + moves: [], + finalBoard: [], + territory: { black: 0, white: 0, neutral: 0 }, + error: "File not found" + }); + continue; + } + + const result = convertTgnToMoveSequence(tgnFile); + results.push(result); + } + + // Output as JSON + console.log(JSON.stringify(results, null, 2)); + }).catch(error => { + console.error("Error initializing parsers:", error); + process.exit(1); + }); +} + + +main(); diff --git a/trigo-web/tools/tgnValidator.ts b/trigo-web/tools/tgnValidator.ts new file mode 100644 index 0000000000000000000000000000000000000000..28b19f75ee903b657ec20edb8558ca146b8f1620 --- /dev/null +++ b/trigo-web/tools/tgnValidator.ts @@ -0,0 +1,363 @@ +#!/usr/bin/env node +/** + * TGN Validator - Validate TGN files by replaying the game + * + * Validates: + * 1. TGN syntax parsing + * 2. Move legality (no occupied positions, no suicide, etc.) + * 3. Game integrity + */ + +import { TrigoGame, validateTGN } from "@inc/trigo/game.js"; +import { calculateTerritory } from "@inc/trigo/gameUtils.js"; +import { initializeParsers } from "@inc/trigo/parserInit.js"; +import { parseTGN } from "@inc/tgn/tgnParser.js"; +import { decodeAb0yz } from "@inc/trigo/ab0yz.js"; +import * as fs from "fs"; +import * as path from "path"; + + +interface ValidationResult { + file: string; + valid: boolean; + parseError?: string; + replayError?: string; + moveCount?: number; + boardShape?: { x: number; y: number; z: number }; + territory?: { + black: number; + white: number; + neutral: number; + }; + metadata?: { + event?: string; + black?: string; + white?: string; + date?: string; + }; +} + + +function validateTgnFile(filePath: string): ValidationResult { + const fileName = path.basename(filePath); + + // Check file exists + if (!fs.existsSync(filePath)) { + return { + file: fileName, + valid: false, + parseError: "File not found" + }; + } + + // Read file + let content: string; + try { + content = fs.readFileSync(filePath, "utf-8"); + } catch (err) { + return { + file: fileName, + valid: false, + parseError: `Failed to read file: ${err instanceof Error ? err.message : String(err)}` + }; + } + + // Validate TGN syntax + const syntaxResult = validateTGN(content); + if (!syntaxResult.valid) { + return { + file: fileName, + valid: false, + parseError: syntaxResult.error + }; + } + + // Replay the game with strict move validation + try { + const parsed = parseTGN(content); + + // Extract board shape + let boardShape: { x: number; y: number; z: number }; + if (parsed.tags.Board && Array.isArray(parsed.tags.Board)) { + const shape = parsed.tags.Board; + boardShape = { + x: shape[0] || 5, + y: shape[1] || 5, + z: shape[2] || 1 + }; + } else { + boardShape = { x: 5, y: 5, z: 5 }; + } + + // Create game and replay moves with strict validation + const game = new TrigoGame(boardShape); + game.startGame(); + + let moveIndex = 0; + if (parsed.moves && parsed.moves.length > 0) { + for (const round of parsed.moves) { + // Validate black's move + if (round.action_black) { + moveIndex++; + const error = applyMoveStrict(game, round.action_black, boardShape, moveIndex, "Black"); + if (error) { + return { + file: fileName, + valid: false, + replayError: error + }; + } + } + + // Validate white's move + if (round.action_white) { + moveIndex++; + const error = applyMoveStrict(game, round.action_white, boardShape, moveIndex, "White"); + if (error) { + return { + file: fileName, + valid: false, + replayError: error + }; + } + } + } + } + + const shape = game.getShape(); + const history = game.getHistory(); + const board = game.getBoard(); + const territory = calculateTerritory(board, shape); + + // Extract metadata from TGN + const metadata: ValidationResult["metadata"] = {}; + const eventMatch = content.match(/\[Event\s+"([^"]+)"\]/); + const blackMatch = content.match(/\[Black\s+"([^"]+)"\]/); + const whiteMatch = content.match(/\[White\s+"([^"]+)"\]/); + const dateMatch = content.match(/\[Date\s+"([^"]+)"\]/); + + if (eventMatch) metadata.event = eventMatch[1]; + if (blackMatch) metadata.black = blackMatch[1]; + if (whiteMatch) metadata.white = whiteMatch[1]; + if (dateMatch) metadata.date = dateMatch[1]; + + return { + file: fileName, + valid: true, + moveCount: history.length, + boardShape: shape, + territory: { + black: territory.black, + white: territory.white, + neutral: territory.neutral + }, + metadata: Object.keys(metadata).length > 0 ? metadata : undefined + }; + } catch (err) { + return { + file: fileName, + valid: false, + replayError: err instanceof Error ? err.message : String(err) + }; + } +} + + +/** + * Apply a move with strict validation - returns error message if invalid + */ +function applyMoveStrict( + game: TrigoGame, + action: { type: string; position?: string }, + boardShape: { x: number; y: number; z: number }, + moveIndex: number, + player: string +): string | null { + if (action.type === "pass") { + game.pass(); + return null; + } else if (action.type === "resign") { + game.surrender(); + return null; + } else if (action.type === "move" && action.position) { + const coords = decodeAb0yz(action.position, [boardShape.x, boardShape.y, boardShape.z]); + const position = { x: coords[0], y: coords[1], z: coords[2] }; + + // Check move validity before applying + const validation = game.isValidMove(position); + if (!validation.valid) { + return `Move ${moveIndex} (${player} at ${action.position}): ${validation.reason}`; + } + + // Apply the move + const success = game.drop(position); + if (!success) { + return `Move ${moveIndex} (${player} at ${action.position}): Move rejected`; + } + + return null; + } + + return `Move ${moveIndex} (${player}): Unknown action type "${action.type}"`; +} + + +function printResult(result: ValidationResult, verbose: boolean): void { + const status = result.valid ? "✓" : "✗"; + const statusColor = result.valid ? "\x1b[32m" : "\x1b[31m"; + const reset = "\x1b[0m"; + + console.log(`${statusColor}${status}${reset} ${result.file}`); + + if (!result.valid) { + if (result.parseError) { + console.log(` Parse error: ${result.parseError}`); + } + if (result.replayError) { + console.log(` Replay error: ${result.replayError}`); + } + } else if (verbose) { + console.log(` Board: ${result.boardShape?.x}×${result.boardShape?.y}×${result.boardShape?.z}`); + console.log(` Moves: ${result.moveCount}`); + if (result.territory) { + console.log(` Territory: B=${result.territory.black}, W=${result.territory.white}, N=${result.territory.neutral}`); + } + if (result.metadata) { + if (result.metadata.event) console.log(` Event: ${result.metadata.event}`); + if (result.metadata.black) console.log(` Black: ${result.metadata.black}`); + if (result.metadata.white) console.log(` White: ${result.metadata.white}`); + if (result.metadata.date) console.log(` Date: ${result.metadata.date}`); + } + } +} + + +/** + * Expand paths - if path is a directory, return all .tgn files in it + */ +function expandPaths(paths: string[]): string[] { + const result: string[] = []; + + for (const p of paths) { + if (!fs.existsSync(p)) { + // Keep non-existent paths - will report as error later + result.push(p); + continue; + } + + const stat = fs.statSync(p); + if (stat.isDirectory()) { + // Find all .tgn files in directory + const files = fs.readdirSync(p) + .filter(f => f.endsWith(".tgn")) + .map(f => path.join(p, f)) + .sort(); + result.push(...files); + } else { + result.push(p); + } + } + + return result; +} + + +function printUsage(): void { + console.log(` +Usage: npx tsx tools/tgnValidator.ts [options] [ ...] + +Arguments: + TGN file or directory containing TGN files + +Options: + --verbose, -v Show detailed information for valid files + --json Output results as JSON + --help, -h Show this help message + +Examples: + npx tsx tools/tgnValidator.ts game.tgn + npx tsx tools/tgnValidator.ts ./games/ # validate all .tgn in directory + npx tsx tools/tgnValidator.ts -v ./games/*.tgn + npx tsx tools/tgnValidator.ts --json ./output/ > results.json +`); +} + + +async function main(): Promise { + const args = process.argv.slice(2); + + // Parse options + let verbose = false; + let jsonOutput = false; + const files: string[] = []; + + for (const arg of args) { + if (arg === "--verbose" || arg === "-v") { + verbose = true; + } else if (arg === "--json") { + jsonOutput = true; + } else if (arg === "--help" || arg === "-h") { + printUsage(); + process.exit(0); + } else if (!arg.startsWith("-")) { + files.push(arg); + } else { + console.error(`Unknown option: ${arg}`); + printUsage(); + process.exit(1); + } + } + + if (files.length === 0) { + console.error("Error: No TGN files specified\n"); + printUsage(); + process.exit(1); + } + + // Expand directories to individual files + const expandedFiles = expandPaths(files); + + if (expandedFiles.length === 0) { + console.error("Error: No TGN files found in specified path(s)\n"); + process.exit(1); + } + + // Initialize parser + await initializeParsers(); + + // Validate all files + const results: ValidationResult[] = []; + for (const file of expandedFiles) { + const result = validateTgnFile(file); + results.push(result); + + process.stdout.write(result.valid ? "." : "!"); + } + + // Output results + if (jsonOutput) { + console.log(JSON.stringify(results, null, 2)); + } else { + console.log(`\nValidating ${expandedFiles.length} TGN file(s)...\n`); + + for (const result of results) { + printResult(result, verbose); + } + + // Summary + const validCount = results.filter(r => r.valid).length; + const invalidCount = results.length - validCount; + + console.log(`\n${"─".repeat(40)}`); + console.log(`Total: ${results.length} file(s)`); + console.log(` Valid: ${validCount}`); + console.log(` Invalid: ${invalidCount}`); + + if (invalidCount > 0) { + process.exit(1); + } + } +} + + +main(); diff --git a/trigo-web/tools/validateGameResults.ts b/trigo-web/tools/validateGameResults.ts new file mode 100644 index 0000000000000000000000000000000000000000..c5de914bee83e2fff7376c244411346645d743c2 --- /dev/null +++ b/trigo-web/tools/validateGameResults.ts @@ -0,0 +1,100 @@ +#!/usr/bin/env node +/** + * TypeScript Game Validation - Output game results as JSON + * + * Reads TGN files and outputs final board state + territory for comparison + */ + +import { TrigoGame } from "@inc/trigo/game.js"; +import { calculateTerritory } from "@inc/trigo/gameUtils.js"; +import * as fs from "fs"; +import * as path from "path"; + + +interface GameResult { + tgnFile: string; + boardShape: { x: number; y: number; z: number }; + moveCount: number; + board: number[][][]; // 3D array: board[x][y][z] = stone type + territory: { + black: number; + white: number; + neutral: number; + }; + error?: string; +} + + +function processGame(tgnFilePath: string): GameResult { + try { + const tgnContent = fs.readFileSync(tgnFilePath, "utf-8"); + + // Parse TGN + const game = TrigoGame.fromTGN(tgnContent); + + // Get final state + const board = game.getBoard(); + const shape = game.getShape(); + const history = game.getHistory(); + + // Calculate territory + const territory = calculateTerritory(board, shape); + + return { + tgnFile: path.basename(tgnFilePath), + boardShape: shape, + moveCount: history.length, + board, + territory: { + black: territory.black, + white: territory.white, + neutral: territory.neutral + } + }; + } catch (error) { + return { + tgnFile: path.basename(tgnFilePath), + boardShape: { x: 0, y: 0, z: 0 }, + moveCount: 0, + board: [], + territory: { black: 0, white: 0, neutral: 0 }, + error: error instanceof Error ? error.message : String(error) + }; + } +} + + +function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: validateGameResults.ts [ ...]"); + process.exit(1); + } + + const results: GameResult[] = []; + + for (const tgnFile of args) { + if (!fs.existsSync(tgnFile)) { + console.error(`File not found: ${tgnFile}`); + results.push({ + tgnFile: path.basename(tgnFile), + boardShape: { x: 0, y: 0, z: 0 }, + moveCount: 0, + board: [], + territory: { black: 0, white: 0, neutral: 0 }, + error: "File not found" + }); + continue; + } + + const result = processGame(tgnFile); + results.push(result); + } + + // Output as JSON + console.log(JSON.stringify(results, null, 2)); +} + + +main(); diff --git a/trigo-web/tools/validate_game_equivalence.ts b/trigo-web/tools/validate_game_equivalence.ts new file mode 100644 index 0000000000000000000000000000000000000000..51d8cf3cdc00d8b36c87e554f9fd68b250fb65da --- /dev/null +++ b/trigo-web/tools/validate_game_equivalence.ts @@ -0,0 +1,97 @@ +#!/usr/bin/env node +/** + * TypeScript side of cross-language equivalence validation + * + * Reads TGN files and outputs: + * - Final board state + * - Territory calculation results + * - Move count + * + * Output format: JSON for easy parsing by comparison script + */ + +import { TrigoGame } from "@inc/trigo/game.js"; +import { calculateTerritory } from "@inc/trigo/gameUtils.js"; +import * as fs from "fs"; +import * as path from "path"; + + +interface GameResult { + tgnFile: string; + boardShape: { x: number; y: number; z: number }; + moveCount: number; + board: number[][][]; // 3D array: board[x][y][z] = stone type + territory: { + black: number; + white: number; + neutral: number; + }; + error?: string; +} + + +function processGame(tgnFile: string): GameResult { + try { + const tgnContent = fs.readFileSync(tgnFile, "utf-8"); + + // Parse TGN + const game = TrigoGame.fromTGN(tgnContent); + + // Get final state + const board = game.getBoard(); + const shape = game.getShape(); + const history = game.getHistory(); + + // Calculate territory + const territory = calculateTerritory(board, shape); + + return { + tgnFile: path.basename(tgnFile), + boardShape: shape, + moveCount: history.length, + board, + territory: { + black: territory.black, + white: territory.white, + neutral: territory.neutral + } + }; + } catch (error) { + return { + tgnFile: path.basename(tgnFile), + boardShape: { x: 0, y: 0, z: 0 }, + moveCount: 0, + board: [], + territory: { black: 0, white: 0, neutral: 0 }, + error: error instanceof Error ? error.message : String(error) + }; + } +} + + +function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.error("Usage: validate_game_equivalence.ts [ ...]"); + process.exit(1); + } + + const results: GameResult[] = []; + + for (const tgnFile of args) { + if (!fs.existsSync(tgnFile)) { + console.error(`File not found: ${tgnFile}`); + continue; + } + + const result = processGame(tgnFile); + results.push(result); + } + + // Output as JSON + console.log(JSON.stringify(results, null, 2)); +} + + +main(); diff --git a/trigo-web/yarn.lock b/trigo-web/yarn.lock index 2df851149e3950df598193872618de89f3d87c4d..9769014b8c69c5030f1be1d4cdb5d57ba5416d5c 100644 --- a/trigo-web/yarn.lock +++ b/trigo-web/yarn.lock @@ -2,26 +2,20 @@ # yarn lockfile v1 -"@acemir/cssom@^0.9.23": - version "0.9.23" - resolved "https://registry.yarnpkg.com/@acemir/cssom/-/cssom-0.9.23.tgz#9930458ccace533c597e1cd90c200edc336ed80a" - integrity sha512-2kJ1HxBKzPLbmhZpxBiTZggjtgCwKg1ma5RHShxvd6zgqhDEdEkzpiwe7jLkI2p2BrZvFCXIihdoMkl1H39VnA== +"@acemir/cssom@^0.9.19": + version "0.9.19" "@asamuzakjp/css-color@^4.0.3": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-4.1.0.tgz#4c8c6f48ed2e5c1ad9cc1aa23c80d665e56dd458" - integrity sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w== + version "4.0.5" dependencies: "@csstools/css-calc" "^2.1.4" "@csstools/css-color-parser" "^3.1.0" "@csstools/css-parser-algorithms" "^3.0.5" "@csstools/css-tokenizer" "^3.0.4" - lru-cache "^11.2.2" + lru-cache "^11.2.1" -"@asamuzakjp/dom-selector@^6.7.4": +"@asamuzakjp/dom-selector@^6.7.3": version "6.7.4" - resolved "https://registry.yarnpkg.com/@asamuzakjp/dom-selector/-/dom-selector-6.7.4.tgz#1b7cafe7793e399f9291de2689fdd2efc01838dd" - integrity sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA== dependencies: "@asamuzakjp/nwsapi" "^2.3.9" bidi-js "^1.0.3" @@ -31,34 +25,32 @@ "@asamuzakjp/nwsapi@^2.3.9": version "2.3.9" - resolved "https://registry.yarnpkg.com/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz#ad5549322dfe9d153d4b4dd6f7ff2ae234b06e24" + resolved "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz" integrity sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q== "@babel/helper-string-parser@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== "@babel/helper-validator-identifier@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz" integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== -"@babel/parser@^7.28.5": +"@babel/parser@^7.28.4": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" - integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== dependencies: "@babel/types" "^7.28.5" "@babel/runtime@^7.21.0": version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz" integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== "@babel/types@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz" integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== dependencies: "@babel/helper-string-parser" "^7.27.1" @@ -66,17 +58,17 @@ "@csstools/color-helpers@^5.1.0": version "5.1.0" - resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.1.0.tgz#106c54c808cabfd1ab4c602d8505ee584c2996ef" + resolved "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz" integrity sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA== "@csstools/css-calc@^2.1.4": version "2.1.4" - resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.4.tgz#8473f63e2fcd6e459838dd412401d5948f224c65" + resolved "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz" integrity sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ== "@csstools/css-color-parser@^3.1.0": version "3.1.0" - resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz#4e386af3a99dd36c46fef013cfe4c1c341eed6f0" + resolved "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz" integrity sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA== dependencies: "@csstools/color-helpers" "^5.1.0" @@ -84,302 +76,130 @@ "@csstools/css-parser-algorithms@^3.0.5": version "3.0.5" - resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz#5755370a9a29abaec5515b43c8b3f2cf9c2e3076" + resolved "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz" integrity sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ== "@csstools/css-syntax-patches-for-csstree@^1.0.14": - version "1.0.16" - resolved "https://registry.yarnpkg.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.16.tgz#327f24b8cc2583565f8d60df16e682b8fa0e9a70" - integrity sha512-2SpS4/UaWQaGpBINyG5ZuCHnUDeVByOhvbkARwfmnfxDvTaj80yOI1cD8Tw93ICV5Fx4fnyDKWQZI1CDtcWyUg== + version "1.0.15" "@csstools/css-tokenizer@^3.0.4": version "3.0.4" - resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz#333fedabc3fd1a8e5d0100013731cf19e6a8c5d3" + resolved "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz" integrity sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw== -"@esbuild/aix-ppc64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" - integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== - -"@esbuild/aix-ppc64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz#80fcbe36130e58b7670511e888b8e88a259ed76c" - integrity sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA== - -"@esbuild/android-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" - integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== - -"@esbuild/android-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz#8aa4965f8d0a7982dc21734bf6601323a66da752" - integrity sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg== - -"@esbuild/android-arm@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" - integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== - -"@esbuild/android-arm@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz#300712101f7f50f1d2627a162e6e09b109b6767a" - integrity sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg== - -"@esbuild/android-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" - integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== - -"@esbuild/android-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz#87dfb27161202bdc958ef48bb61b09c758faee16" - integrity sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg== - -"@esbuild/darwin-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" - integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== - -"@esbuild/darwin-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz#79197898ec1ff745d21c071e1c7cc3c802f0c1fd" - integrity sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg== - -"@esbuild/darwin-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" - integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== - -"@esbuild/darwin-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz#146400a8562133f45c4d2eadcf37ddd09718079e" - integrity sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA== - -"@esbuild/freebsd-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" - integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== - -"@esbuild/freebsd-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz#1c5f9ba7206e158fd2b24c59fa2d2c8bb47ca0fe" - integrity sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg== - -"@esbuild/freebsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" - integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== - -"@esbuild/freebsd-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz#ea631f4a36beaac4b9279fa0fcc6ca29eaeeb2b3" - integrity sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ== - -"@esbuild/linux-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" - integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== - -"@esbuild/linux-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz#e1066bce58394f1b1141deec8557a5f0a22f5977" - integrity sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ== - -"@esbuild/linux-arm@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" - integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== - -"@esbuild/linux-arm@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz#452cd66b20932d08bdc53a8b61c0e30baf4348b9" - integrity sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw== - -"@esbuild/linux-ia32@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" - integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== - -"@esbuild/linux-ia32@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz#b24f8acc45bcf54192c7f2f3be1b53e6551eafe0" - integrity sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA== - -"@esbuild/linux-loong64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" - integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== - -"@esbuild/linux-loong64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz#f9cfffa7fc8322571fbc4c8b3268caf15bd81ad0" - integrity sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng== - -"@esbuild/linux-mips64el@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" - integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== - -"@esbuild/linux-mips64el@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz#575a14bd74644ffab891adc7d7e60d275296f2cd" - integrity sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw== - -"@esbuild/linux-ppc64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" - integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== - -"@esbuild/linux-ppc64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz#75b99c70a95fbd5f7739d7692befe60601591869" - integrity sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA== - -"@esbuild/linux-riscv64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" - integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== - -"@esbuild/linux-riscv64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz#2e3259440321a44e79ddf7535c325057da875cd6" - integrity sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w== - -"@esbuild/linux-s390x@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" - integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== - -"@esbuild/linux-s390x@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz#17676cabbfe5928da5b2a0d6df5d58cd08db2663" - integrity sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg== - "@esbuild/linux-x64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz" integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== "@esbuild/linux-x64@0.25.12": version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz#0583775685ca82066d04c3507f09524d3cd7a306" + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz" integrity sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw== -"@esbuild/netbsd-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz#f04c4049cb2e252fe96b16fed90f70746b13f4a4" - integrity sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg== - -"@esbuild/netbsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" - integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== - -"@esbuild/netbsd-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz#77da0d0a0d826d7c921eea3d40292548b258a076" - integrity sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ== +"@eslint-community/eslint-utils@^4.8.0": + version "4.9.0" + dependencies: + eslint-visitor-keys "^3.4.3" -"@esbuild/openbsd-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz#6296f5867aedef28a81b22ab2009c786a952dccd" - integrity sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A== +"@eslint-community/regexpp@^4.12.1": + version "4.12.2" -"@esbuild/openbsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" - integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== +"@eslint/config-array@^0.21.1": + version "0.21.1" + dependencies: + "@eslint/object-schema" "^2.1.7" + debug "^4.3.1" + minimatch "^3.1.2" -"@esbuild/openbsd-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz#f8d23303360e27b16cf065b23bbff43c14142679" - integrity sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw== +"@eslint/config-helpers@^0.4.1": + version "0.4.1" + dependencies: + "@eslint/core" "^0.16.0" -"@esbuild/openharmony-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz#49e0b768744a3924be0d7fd97dd6ce9b2923d88d" - integrity sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg== +"@eslint/core@^0.16.0": + version "0.16.0" + dependencies: + "@types/json-schema" "^7.0.15" -"@esbuild/sunos-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" - integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== +"@eslint/eslintrc@^3.3.1": + version "3.3.1" + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" -"@esbuild/sunos-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz#a6ed7d6778d67e528c81fb165b23f4911b9b13d6" - integrity sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w== +"@eslint/js@9.38.0": + version "9.38.0" -"@esbuild/win32-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" - integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== +"@eslint/object-schema@^2.1.7": + version "2.1.7" -"@esbuild/win32-arm64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz#9ac14c378e1b653af17d08e7d3ce34caef587323" - integrity sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg== +"@eslint/plugin-kit@^0.4.0": + version "0.4.0" + dependencies: + "@eslint/core" "^0.16.0" + levn "^0.4.1" -"@esbuild/win32-ia32@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" - integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== +"@humanfs/core@^0.19.1": + version "0.19.1" -"@esbuild/win32-ia32@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz#918942dcbbb35cc14fca39afb91b5e6a3d127267" - integrity sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ== +"@humanfs/node@^0.16.6": + version "0.16.7" + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.4.0" -"@esbuild/win32-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" - integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" -"@esbuild/win32-x64@0.25.12": - version "0.25.12" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz#9bdad8176be7811ad148d1f8772359041f46c6c5" - integrity sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA== +"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": + version "0.4.3" "@jridgewell/sourcemap-codec@^1.5.5": version "1.5.5" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== "@pkgr/core@^0.2.9": version "0.2.9" - resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.9.tgz#d229a7b7f9dac167a156992ef23c7f023653f53b" + resolved "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz" integrity sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA== "@polka/url@^1.0.0-next.24": version "1.0.0-next.29" - resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.29.tgz#5a40109a1ab5f84d6fd8fc928b19f367cbe7e7b1" + resolved "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz" integrity sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz" integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== "@protobufjs/base64@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz" integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== "@protobufjs/codegen@^2.0.4": version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz" integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== "@protobufjs/eventemitter@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz" integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== "@protobufjs/fetch@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz" integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== dependencies: "@protobufjs/aspromise" "^1.1.1" @@ -387,147 +207,43 @@ "@protobufjs/float@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz" integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== "@protobufjs/inquire@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz" integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== "@protobufjs/path@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz" integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== "@protobufjs/pool@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz" integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== "@protobufjs/utf8@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== -"@rollup/rollup-android-arm-eabi@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz#7e478b66180c5330429dd161bf84dad66b59c8eb" - integrity sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w== - -"@rollup/rollup-android-arm64@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz#2b025510c53a5e3962d3edade91fba9368c9d71c" - integrity sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w== - -"@rollup/rollup-darwin-arm64@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz#3577c38af68ccf34c03e84f476bfd526abca10a0" - integrity sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA== - -"@rollup/rollup-darwin-x64@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz#2bf5f2520a1f3b551723d274b9669ba5b75ed69c" - integrity sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ== - -"@rollup/rollup-freebsd-arm64@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz#4bb9cc80252564c158efc0710153c71633f1927c" - integrity sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w== - -"@rollup/rollup-freebsd-x64@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz#2301289094d49415a380cf942219ae9d8b127440" - integrity sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q== - -"@rollup/rollup-linux-arm-gnueabihf@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz#1d03d776f2065e09fc141df7d143476e94acca88" - integrity sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw== - -"@rollup/rollup-linux-arm-musleabihf@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz#8623de0e040b2fd52a541c602688228f51f96701" - integrity sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg== - -"@rollup/rollup-linux-arm64-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz#ce2d1999bc166277935dde0301cde3dd0417fb6e" - integrity sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w== - -"@rollup/rollup-linux-arm64-musl@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz#88c2523778444da952651a2219026416564a4899" - integrity sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A== - -"@rollup/rollup-linux-loong64-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz#578ca2220a200ac4226c536c10c8cc6e4f276714" - integrity sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g== - -"@rollup/rollup-linux-ppc64-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz#aa338d3effd4168a20a5023834a74ba2c3081293" - integrity sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw== - -"@rollup/rollup-linux-riscv64-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz#16ba582f9f6cff58119aa242782209b1557a1508" - integrity sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g== - -"@rollup/rollup-linux-riscv64-musl@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz#e404a77ebd6378483888b8064c703adb011340ab" - integrity sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A== - -"@rollup/rollup-linux-s390x-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz#92ad52d306227c56bec43d96ad2164495437ffe6" - integrity sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg== - -"@rollup/rollup-linux-x64-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz#fd0dea3bb9aa07e7083579f25e1c2285a46cb9fa" - integrity sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w== - -"@rollup/rollup-linux-x64-musl@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz#37a3efb09f18d555f8afc490e1f0444885de8951" - integrity sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q== - -"@rollup/rollup-openharmony-arm64@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz#c489bec9f4f8320d42c9b324cca220c90091c1f7" - integrity sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw== - -"@rollup/rollup-win32-arm64-msvc@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz#152832b5f79dc22d1606fac3db946283601b7080" - integrity sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw== - -"@rollup/rollup-win32-ia32-msvc@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz#54d91b2bb3bf3e9f30d32b72065a4e52b3a172a5" - integrity sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA== - -"@rollup/rollup-win32-x64-gnu@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz#df9df03e61a003873efec8decd2034e7f135c71e" - integrity sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg== - -"@rollup/rollup-win32-x64-msvc@4.53.3": - version "4.53.3" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz#38ae84f4c04226c1d56a3b17296ef1e0460ecdfe" - integrity sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ== +"@rollup/rollup-linux-x64-gnu@4.52.5": + version "4.52.5" + +"@rollup/rollup-linux-x64-musl@4.52.5": + version "4.52.5" "@standard-schema/spec@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c" + resolved "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz" integrity sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA== "@types/chai@^5.2.2": version "5.2.3" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-5.2.3.tgz#8e9cd9e1c3581fa6b341a5aed5588eb285be0b4a" + resolved "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz" integrity sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA== dependencies: "@types/deep-eql" "*" @@ -535,94 +251,79 @@ "@types/deep-eql@*": version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd" + resolved "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz" integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== -"@types/estree@1.0.8", "@types/estree@^1.0.0": +"@types/estree@^1.0.0", "@types/estree@^1.0.6", "@types/estree@1.0.8": version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== -"@types/node@>=13.7.0", "@types/node@^24.10.0": - version "24.10.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.10.1.tgz#91e92182c93db8bd6224fca031e2370cef9a8f01" - integrity sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ== +"@types/json-schema@^7.0.15": + version "7.0.15" + +"@types/node@^18.0.0 || >=20.0.0", "@types/node@^20.0.0 || ^22.0.0 || >=24.0.0", "@types/node@^20.19.0 || >=22.12.0", "@types/node@^24.10.0", "@types/node@>=13.7.0": + version "24.10.0" dependencies: undici-types "~7.16.0" "@types/yargs-parser@*": version "21.0.3" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^17.0.34": - version "17.0.35" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.35.tgz#07013e46aa4d7d7d50a49e15604c1c5340d4eb24" - integrity sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg== + version "17.0.34" dependencies: "@types/yargs-parser" "*" "@vitejs/plugin-vue@^5.2.4": version "5.2.4" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz#9e8a512eb174bfc2a333ba959bbf9de428d89ad8" + resolved "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz" integrity sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA== -"@vitest/expect@4.0.12": - version "4.0.12" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-4.0.12.tgz#151a425f06707bc8ff07078b23121832da8d7c71" - integrity sha512-is+g0w8V3/ZhRNrRizrJNr8PFQKwYmctWlU4qg8zy5r9aIV5w8IxXLlfbbxJCwSpsVl2PXPTm2/zruqTqz3QSg== +"@vitest/expect@4.0.6": + version "4.0.6" dependencies: "@standard-schema/spec" "^1.0.0" "@types/chai" "^5.2.2" - "@vitest/spy" "4.0.12" - "@vitest/utils" "4.0.12" - chai "^6.2.1" + "@vitest/spy" "4.0.6" + "@vitest/utils" "4.0.6" + chai "^6.0.1" tinyrainbow "^3.0.3" -"@vitest/mocker@4.0.12": - version "4.0.12" - resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-4.0.12.tgz#fbd117560a11691eeb20c3cd9f6d3e39acdf6433" - integrity sha512-GsmA/tD5Ht3RUFoz41mZsMU1AXch3lhmgbTnoSPTdH231g7S3ytNN1aU0bZDSyxWs8WA7KDyMPD5L4q6V6vj9w== +"@vitest/mocker@4.0.6": + version "4.0.6" dependencies: - "@vitest/spy" "4.0.12" + "@vitest/spy" "4.0.6" estree-walker "^3.0.3" - magic-string "^0.30.21" + magic-string "^0.30.19" -"@vitest/pretty-format@4.0.12": - version "4.0.12" - resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-4.0.12.tgz#93e152204551107c86dba67d2512cc9a0b77c6c0" - integrity sha512-R7nMAcnienG17MvRN8TPMJiCG8rrZJblV9mhT7oMFdBXvS0x+QD6S1G4DxFusR2E0QIS73f7DqSR1n87rrmE+g== +"@vitest/pretty-format@4.0.6": + version "4.0.6" dependencies: tinyrainbow "^3.0.3" -"@vitest/runner@4.0.12": - version "4.0.12" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-4.0.12.tgz#edf824825630242ce2e1115b6210787dae06ead3" - integrity sha512-hDlCIJWuwlcLumfukPsNfPDOJokTv79hnOlf11V+n7E14rHNPz0Sp/BO6h8sh9qw4/UjZiKyYpVxK2ZNi+3ceQ== +"@vitest/runner@4.0.6": + version "4.0.6" dependencies: - "@vitest/utils" "4.0.12" + "@vitest/utils" "4.0.6" pathe "^2.0.3" -"@vitest/snapshot@4.0.12": - version "4.0.12" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-4.0.12.tgz#ac731df3a02e6447542e763f58eff401bcca6fa6" - integrity sha512-2jz9zAuBDUSbnfyixnyOd1S2YDBrZO23rt1bicAb6MA/ya5rHdKFRikPIDpBj/Dwvh6cbImDmudegnDAkHvmRQ== +"@vitest/snapshot@4.0.6": + version "4.0.6" dependencies: - "@vitest/pretty-format" "4.0.12" - magic-string "^0.30.21" + "@vitest/pretty-format" "4.0.6" + magic-string "^0.30.19" pathe "^2.0.3" -"@vitest/spy@4.0.12": - version "4.0.12" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-4.0.12.tgz#23ec14a1fe6827b6993db042e43616bc6360d1fb" - integrity sha512-GZjI9PPhiOYNX8Nsyqdw7JQB+u0BptL5fSnXiottAUBHlcMzgADV58A7SLTXXQwcN1yZ6gfd1DH+2bqjuUlCzw== +"@vitest/spy@4.0.6": + version "4.0.6" -"@vitest/ui@^4.0.6": - version "4.0.12" - resolved "https://registry.yarnpkg.com/@vitest/ui/-/ui-4.0.12.tgz#cd926c53e8cb1d45eb001c4a6a523e529d3659ed" - integrity sha512-RCqeApCnbwd5IFvxk6OeKMXTvzHU/cVqY8HAW0gWk0yAO6wXwQJMKhDfDtk2ss7JCy9u7RNC3kyazwiaDhBA/g== +"@vitest/ui@^4.0.6", "@vitest/ui@4.0.6": + version "4.0.6" dependencies: - "@vitest/utils" "4.0.12" + "@vitest/utils" "4.0.6" fflate "^0.8.2" flatted "^3.3.3" pathe "^2.0.3" @@ -630,81 +331,69 @@ tinyglobby "^0.2.15" tinyrainbow "^3.0.3" -"@vitest/utils@4.0.12": - version "4.0.12" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-4.0.12.tgz#5b89eb454906eab14fb7c05cb180f066be6b5be7" - integrity sha512-DVS/TLkLdvGvj1avRy0LSmKfrcI9MNFvNGN6ECjTUHWJdlcgPDOXhjMis5Dh7rBH62nAmSXnkPbE+DZ5YD75Rw== +"@vitest/utils@4.0.6": + version "4.0.6" dependencies: - "@vitest/pretty-format" "4.0.12" + "@vitest/pretty-format" "4.0.6" tinyrainbow "^3.0.3" "@volar/language-core@2.4.23": version "2.4.23" - resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-2.4.23.tgz#deb6dbc5fdbafa9bb7ba691fc59cb196cdb856d3" + resolved "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz" integrity sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ== dependencies: "@volar/source-map" "2.4.23" "@volar/source-map@2.4.23": version "2.4.23" - resolved "https://registry.yarnpkg.com/@volar/source-map/-/source-map-2.4.23.tgz#d476e11a3a669d89858a5eb38b02342be39b0e44" + resolved "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz" integrity sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q== "@volar/typescript@2.4.23": version "2.4.23" - resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-2.4.23.tgz#b9b114ea01ad0ad977139edda0239fdafdb21ad7" + resolved "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz" integrity sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag== dependencies: "@volar/language-core" "2.4.23" path-browserify "^1.0.1" vscode-uri "^3.0.8" -"@vue/compiler-core@3.5.24": - version "3.5.24" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.24.tgz#1853f4b7d7090033cd9041aab6e7e8017d66c39c" - integrity sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig== +"@vue/compiler-core@3.5.22": + version "3.5.22" dependencies: - "@babel/parser" "^7.28.5" - "@vue/shared" "3.5.24" + "@babel/parser" "^7.28.4" + "@vue/shared" "3.5.22" entities "^4.5.0" estree-walker "^2.0.2" source-map-js "^1.2.1" -"@vue/compiler-dom@3.5.24", "@vue/compiler-dom@^3.5.0": - version "3.5.24" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz#b02e98749c377d6d2ba30dc2e94ce0f5b0af060c" - integrity sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw== +"@vue/compiler-dom@^3.5.0", "@vue/compiler-dom@3.5.22": + version "3.5.22" dependencies: - "@vue/compiler-core" "3.5.24" - "@vue/shared" "3.5.24" + "@vue/compiler-core" "3.5.22" + "@vue/shared" "3.5.22" -"@vue/compiler-sfc@3.5.24": - version "3.5.24" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz#7cc3329b672b7b75d04a71eabe81bc84209dfa9f" - integrity sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g== +"@vue/compiler-sfc@3.5.22": + version "3.5.22" dependencies: - "@babel/parser" "^7.28.5" - "@vue/compiler-core" "3.5.24" - "@vue/compiler-dom" "3.5.24" - "@vue/compiler-ssr" "3.5.24" - "@vue/shared" "3.5.24" + "@babel/parser" "^7.28.4" + "@vue/compiler-core" "3.5.22" + "@vue/compiler-dom" "3.5.22" + "@vue/compiler-ssr" "3.5.22" + "@vue/shared" "3.5.22" estree-walker "^2.0.2" - magic-string "^0.30.21" + magic-string "^0.30.19" postcss "^8.5.6" source-map-js "^1.2.1" -"@vue/compiler-ssr@3.5.24": - version "3.5.24" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz#3891f2479928751353b946e491e048f33d0249a8" - integrity sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg== +"@vue/compiler-ssr@3.5.22": + version "3.5.22" dependencies: - "@vue/compiler-dom" "3.5.24" - "@vue/shared" "3.5.24" + "@vue/compiler-dom" "3.5.22" + "@vue/shared" "3.5.22" -"@vue/language-core@3.1.4": - version "3.1.4" - resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-3.1.4.tgz#17ae7a20b0c401a8665655366af4e0ce4980344a" - integrity sha512-n/58wm8SkmoxMWkUNUH/PwoovWe4hmdyPJU2ouldr3EPi1MLoS7iDN46je8CsP95SnVBs2axInzRglPNKvqMcg== +"@vue/language-core@3.1.3": + version "3.1.3" dependencies: "@volar/language-core" "2.4.23" "@vue/compiler-dom" "^3.5.0" @@ -714,171 +403,164 @@ path-browserify "^1.0.1" picomatch "^4.0.2" -"@vue/reactivity@3.5.24": - version "3.5.24" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.24.tgz#d2de10045f3ec05108787b6c90701b3f3bc401f8" - integrity sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg== +"@vue/reactivity@3.5.22": + version "3.5.22" dependencies: - "@vue/shared" "3.5.24" + "@vue/shared" "3.5.22" -"@vue/runtime-core@3.5.24": - version "3.5.24" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.24.tgz#902ff6f6372a14b190aeb501a57467759bc58241" - integrity sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ== +"@vue/runtime-core@3.5.22": + version "3.5.22" dependencies: - "@vue/reactivity" "3.5.24" - "@vue/shared" "3.5.24" + "@vue/reactivity" "3.5.22" + "@vue/shared" "3.5.22" -"@vue/runtime-dom@3.5.24": - version "3.5.24" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.24.tgz#b27414fd0be2c9b58c701a67ba4db96b78819e9b" - integrity sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw== +"@vue/runtime-dom@3.5.22": + version "3.5.22" dependencies: - "@vue/reactivity" "3.5.24" - "@vue/runtime-core" "3.5.24" - "@vue/shared" "3.5.24" + "@vue/reactivity" "3.5.22" + "@vue/runtime-core" "3.5.22" + "@vue/shared" "3.5.22" csstype "^3.1.3" -"@vue/server-renderer@3.5.24": - version "3.5.24" - resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.24.tgz#efc5f8dc2776b91f04bf3249be95afe6e83a02a8" - integrity sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w== +"@vue/server-renderer@3.5.22": + version "3.5.22" dependencies: - "@vue/compiler-ssr" "3.5.24" - "@vue/shared" "3.5.24" + "@vue/compiler-ssr" "3.5.22" + "@vue/shared" "3.5.22" -"@vue/shared@3.5.24", "@vue/shared@^3.5.0": - version "3.5.24" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.24.tgz#45ea9e6e037e53cfb8141ffa6bcad75b8be11e9c" - integrity sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A== +"@vue/shared@^3.5.0", "@vue/shared@3.5.22": + version "3.5.22" -JSONSelect@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/JSONSelect/-/JSONSelect-0.4.0.tgz#a08edcc67eb3fcbe99ed630855344a0cf282bb8d" - integrity sha512-VRLR3Su35MH+XV2lrvh9O7qWoug/TUyj9tLDjn9rtpUCNnILLrHjgd/tB0KrhugCxUpj3UqoLqfYb3fLJdIQQQ== +acorn-jsx@^5.3.2: + version "5.3.2" -"JSV@>= 4.0.x": - version "4.0.2" - resolved "https://registry.yarnpkg.com/JSV/-/JSV-4.0.2.tgz#d077f6825571f82132f9dffaed587b4029feff57" - integrity sha512-ZJ6wx9xaKJ3yFUhq5/sk82PJMuUyLk277I8mQeyDgCTjGdjWJIvPfaU5LIXaMuaN2UO1X3kZH4+lgphublZUHw== +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.15.0: + version "8.15.0" adm-zip@^0.5.16: version "0.5.16" - resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.16.tgz#0b5e4c779f07dedea5805cdccb1147071d94a909" + resolved "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz" integrity sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ== agent-base@^7.1.0, agent-base@^7.1.2: version "7.1.4" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz" integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== +ajv@^6.12.4: + version "6.12.6" + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + alien-signals@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/alien-signals/-/alien-signals-3.1.0.tgz#06ec32567f1724daed24db768b76eecd8884a175" + resolved "https://registry.npmjs.org/alien-signals/-/alien-signals-3.1.0.tgz" integrity sha512-yufC6VpSy8tK3I0lO67pjumo5JvDQVQyr38+3OHqe6CHl1t2VZekKZ7EKKZSqk0cRmE7U7tfZbpXiKNzuc+ckg== amdefine@>=0.0.4: version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + resolved "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz" integrity sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg== ansi-escapes@^7.0.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.2.0.tgz#31b25afa3edd3efc09d98c2fee831d460ff06b49" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz" integrity sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw== dependencies: environment "^1.0.0" ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: version "6.2.2" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz" integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^6.2.1: version "6.2.3" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz" integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== -ansi-styles@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178" - integrity sha512-3iF4FIKdxaVYT3JqQuY3Wat/T2t7TRbbQ94Fu50ZUCbLy4TFbTzr90NOHQodQkNqmeEGCw8WbeP78WNi6SKYUA== +argparse@^2.0.1: + version "2.0.1" assertion-error@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz" integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== +balanced-match@^1.0.0: + version "1.0.2" + bidi-js@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/bidi-js/-/bidi-js-1.0.3.tgz#6f8bcf3c877c4d9220ddf49b9bb6930c88f877d2" + resolved "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz" integrity sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw== dependencies: require-from-string "^2.0.2" boolean@^3.0.1: version "3.2.0" - resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" + resolved "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz" integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== +brace-expansion@^1.1.7: + version "1.1.12" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + braces@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" -chai@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/chai/-/chai-6.2.1.tgz#d1e64bc42433fbee6175ad5346799682060b5b6a" - integrity sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg== +callsites@^3.0.0: + version "3.1.0" + +chai@^6.0.1: + version "6.2.0" -chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" - integrity sha512-sQfYDlfv2DGVtjdoQqxS0cEZDroyG8h6TamA6rvxwlrU5BaSLDx9xhatBYl2pxZ7gmpNaPFVwBtdGdu5rQ+tYQ== - dependencies: - ansi-styles "~1.0.0" - has-color "~0.1.0" - strip-ansi "~0.1.0" - cjson@0.3.0: version "0.3.0" - resolved "https://registry.yarnpkg.com/cjson/-/cjson-0.3.0.tgz#e6439b90703d312ff6e2224097bea92ce3d02a14" + resolved "https://registry.npmjs.org/cjson/-/cjson-0.3.0.tgz" integrity sha512-bBRQcCIHzI1IVH59fR0bwGrFmi3Btb/JNwM/n401i1DnYgWndpsUBiQRAddLflkZage20A2d25OAWZZk0vBRlA== dependencies: jsonlint "1.6.0" cli-cursor@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-5.0.0.tgz#24a4831ecf5a6b01ddeb32fb71a4b2088b0dce38" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz" integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== dependencies: restore-cursor "^5.0.0" cli-truncate@^5.0.0: version "5.1.1" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-5.1.1.tgz#455476face9904d94b7d11e98d9adbca15292ea5" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz" integrity sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A== dependencies: slice-ansi "^7.1.0" @@ -886,7 +568,7 @@ cli-truncate@^5.0.0: cliui@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" @@ -895,7 +577,7 @@ cliui@^8.0.1: cliui@^9.0.1: version "9.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-9.0.1.tgz#6f7890f386f6f1f79953adc1f78dec46fcc2d291" + resolved "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz" integrity sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w== dependencies: string-width "^7.2.0" @@ -904,34 +586,37 @@ cliui@^9.0.1: color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@~1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^2.0.20: version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== colors@0.5.x: version "0.5.1" - resolved "https://registry.yarnpkg.com/colors/-/colors-0.5.1.tgz#7d0023eaeb154e8ee9fce75dcb923d0ed1667774" + resolved "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz" integrity sha512-XjsuUwpDeY98+yz959OlUK6m7mLBM+1MEG5oaenfuQnNnrQk1WvtcvFgN3FNDP3f2NmZ211t0mNEfSEN1h0eIg== commander@^14.0.2: version "14.0.2" - resolved "https://registry.yarnpkg.com/commander/-/commander-14.0.2.tgz#b71fd37fe4069e4c3c7c13925252ada4eba14e8e" + resolved "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz" integrity sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ== +concat-map@0.0.1: + version "0.0.1" + concurrently@^7.6.0: version "7.6.0" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-7.6.0.tgz#531a6f5f30cf616f355a4afb8f8fcb2bba65a49a" + resolved "https://registry.npmjs.org/concurrently/-/concurrently-7.6.0.tgz" integrity sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw== dependencies: chalk "^4.1.0" @@ -944,31 +629,34 @@ concurrently@^7.6.0: tree-kill "^1.2.2" yargs "^17.3.1" +cross-spawn@^7.0.6: + version "7.0.6" + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + css-tree@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-3.1.0.tgz#7aabc035f4e66b5c86f54570d55e05b1346eb0fd" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz" integrity sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w== dependencies: mdn-data "2.12.2" source-map-js "^1.0.1" -cssstyle@^5.3.3: - version "5.3.3" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-5.3.3.tgz#977f3868f379c17d619e9672839f9b5bb3db9861" - integrity sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw== +cssstyle@^5.3.2: + version "5.3.2" dependencies: "@asamuzakjp/css-color" "^4.0.3" "@csstools/css-syntax-patches-for-csstree" "^1.0.14" css-tree "^3.1.0" csstype@^3.1.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" - integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + version "3.1.3" data-urls@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-6.0.0.tgz#95a7943c8ac14c1d563b771f2621cc50e8ec7744" + resolved "https://registry.npmjs.org/data-urls/-/data-urls-6.0.0.tgz" integrity sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA== dependencies: whatwg-mimetype "^4.0.0" @@ -976,26 +664,29 @@ data-urls@^6.0.0: date-fns@^2.29.1: version "2.30.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" + resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz" integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== dependencies: "@babel/runtime" "^7.21.0" -debug@4, debug@^4.3.4, debug@^4.4.3: +debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.3, debug@4: version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" decimal.js@^10.6.0: version "10.6.0" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a" + resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz" integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg== +deep-is@^0.1.3: + version "0.1.4" + define-data-property@^1.0.1: version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== dependencies: es-define-property "^1.0.0" @@ -1004,7 +695,7 @@ define-data-property@^1.0.1: define-properties@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== dependencies: define-data-property "^1.0.1" @@ -1013,62 +704,67 @@ define-properties@^1.2.1: detect-node@^2.0.4: version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== +dotenv@^17.2.3: + version "17.2.3" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz" + integrity sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w== + ebnf-parser@0.1.10: version "0.1.10" - resolved "https://registry.yarnpkg.com/ebnf-parser/-/ebnf-parser-0.1.10.tgz#cd1f6ba477c5638c40c97ed9b572db5bab5d8331" + resolved "https://registry.npmjs.org/ebnf-parser/-/ebnf-parser-0.1.10.tgz" integrity sha512-urvSxVQ6XJcoTpc+/x2pWhhuOX4aljCNQpwzw+ifZvV1andZkAmiJc3Rq1oGEAQmcjiLceyMXOy1l8ms8qs2fQ== emoji-regex@^10.3.0: version "10.6.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.6.0.tgz#bf3d6e8f7f8fd22a65d9703475bc0147357a6b0d" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz" integrity sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== entities@^4.5.0: version "4.5.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== entities@^6.0.0: version "6.0.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694" + resolved "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz" integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g== environment@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" + resolved "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz" integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== es-define-property@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== es-errors@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-module-lexer@^1.7.0: version "1.7.0" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz" integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== es6-error@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + resolved "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== esbuild@^0.21.3: version "0.21.5" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz" integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== optionalDependencies: "@esbuild/aix-ppc64" "0.21.5" @@ -1097,7 +793,7 @@ esbuild@^0.21.3: esbuild@^0.25.0, esbuild@~0.25.0: version "0.25.12" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.12.tgz#97a1d041f4ab00c2fce2f838d2b9969a2d2a97a5" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz" integrity sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg== optionalDependencies: "@esbuild/aix-ppc64" "0.25.12" @@ -1129,17 +825,17 @@ esbuild@^0.25.0, esbuild@~0.25.0: escalade@^3.1.1: version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== escodegen@1.3.x: version "1.3.3" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.3.3.tgz#f024016f5a88e046fd12005055e939802e6c5f23" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz" integrity sha512-z9FWgKc48wjMlpzF5ymKS1AF8OIgnKLp9VyN7KbdtyrP/9lndwUFqCtMm+TAJmJf7KJFFYc4cFJfVTTGkKEwsA== dependencies: esprima "~1.1.1" @@ -1148,113 +844,212 @@ escodegen@1.3.x: optionalDependencies: source-map "~0.1.33" -eslint-config-prettier@^10.1.8: +eslint-config-prettier@^10.1.8, "eslint-config-prettier@>= 7.0.0 <10.0.0 || >=10.1.0": version "10.1.8" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz#15734ce4af8c2778cc32f0b01b37b0b5cd1ecb97" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz" integrity sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w== eslint-plugin-prettier@^5.5.4: version "5.5.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz#9d61c4ea11de5af704d4edf108c82ccfa7f2e61c" + resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz" integrity sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg== dependencies: prettier-linter-helpers "^1.0.0" synckit "^0.11.7" -esprima@1.1.x, esprima@~1.1.1: +eslint-scope@^8.4.0: + version "8.4.0" + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.3: + version "3.4.3" + +eslint-visitor-keys@^4.2.1: + version "4.2.1" + +"eslint@^6.0.0 || ^7.0.0 || >=8.0.0", eslint@>=7.0.0, eslint@>=8.0.0: + version "9.38.0" + dependencies: + "@eslint-community/eslint-utils" "^4.8.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.21.1" + "@eslint/config-helpers" "^0.4.1" + "@eslint/core" "^0.16.0" + "@eslint/eslintrc" "^3.3.1" + "@eslint/js" "9.38.0" + "@eslint/plugin-kit" "^0.4.0" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.2" + "@types/estree" "^1.0.6" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.4.0" + eslint-visitor-keys "^4.2.1" + espree "^10.4.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + +espree@^10.0.1, espree@^10.4.0: + version "10.4.0" + dependencies: + acorn "^8.15.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.1" + +esprima@~1.1.1, esprima@1.1.x: version "1.1.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.1.1.tgz#5b6f1547f4d102e670e140c509be6771d6aeb549" + resolved "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz" integrity sha512-qxxB994/7NtERxgXdFgLHIs9M6bhLXc6qtUmWZ3L8+gTQ9qaoyki2887P2IqAYsoENyr8SUbTutStDniOHSDHg== +esquery@^1.5.0: + version "1.6.0" + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + estraverse@~1.5.0: version "1.5.1" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.5.1.tgz#867a3e8e58a9f84618afb6c2ddbcd916b7cbaf71" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz" integrity sha512-FpCjJDfmo3vsc/1zKSeqR5k42tcIhxFIlvq+h9j0fO2q/h2uLKyweq7rYJ+0CoVvrGQOxIS5wyBrW/+vF58BUQ== estree-walker@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== estree-walker@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz" integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== dependencies: "@types/estree" "^1.0.0" +esutils@^2.0.2: + version "2.0.3" + esutils@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.0.0.tgz#8151d358e20c8acc7fb745e7472c0025fe496570" + resolved "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz" integrity sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg== eventemitter3@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== expect-type@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.2.tgz#c030a329fb61184126c8447585bc75a7ec6fbff3" + resolved "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz" integrity sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA== +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + fast-diff@^1.1.2: version "1.3.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + +fast-levenshtein@^2.0.6: + version "2.0.6" + fdir@^6.5.0: version "6.5.0" - resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + resolved "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz" integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== fflate@^0.8.2: version "0.8.2" - resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" + resolved "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz" integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== +file-entry-cache@^8.0.0: + version "8.0.0" + dependencies: + flat-cache "^4.0.0" + fill-range@^7.1.1: version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" +find-up@^5.0.0: + version "5.0.0" + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + flatbuffers@^25.1.24: version "25.9.23" - resolved "https://registry.yarnpkg.com/flatbuffers/-/flatbuffers-25.9.23.tgz#346811557fe9312ab5647535e793c761e9c81eb1" + resolved "https://registry.npmjs.org/flatbuffers/-/flatbuffers-25.9.23.tgz" integrity sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ== -flatted@^3.3.3: +flatted@^3.2.9, flatted@^3.3.3: version "3.3.3" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz" integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== -fsevents@~2.3.2, fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-east-asian-width@^1.0.0, get-east-asian-width@^1.3.0, get-east-asian-width@^1.3.1: version "1.4.0" - resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz#9bc4caa131702b4b61729cb7e42735bc550c9ee6" + resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz" integrity sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q== get-tsconfig@^4.7.5: version "4.13.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.0.tgz#fcdd991e6d22ab9a600f00e91c318707a5d9a0d7" + resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz" integrity sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ== dependencies: resolve-pkg-maps "^1.0.0" +glob-parent@^6.0.2: + version "6.0.2" + dependencies: + is-glob "^4.0.3" + global-agent@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6" + resolved "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz" integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q== dependencies: boolean "^3.0.1" @@ -1264,9 +1059,12 @@ global-agent@^3.0.0: semver "^7.3.2" serialize-error "^7.0.1" +globals@^14.0.0: + version "14.0.0" + globalthis@^1.0.1: version "1.0.4" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz" integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== dependencies: define-properties "^1.2.1" @@ -1274,41 +1072,36 @@ globalthis@^1.0.1: gopd@^1.0.1: version "1.2.0" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== guid-typescript@^1.0.9: version "1.0.9" - resolved "https://registry.yarnpkg.com/guid-typescript/-/guid-typescript-1.0.9.tgz#e35f77003535b0297ea08548f5ace6adb1480ddc" + resolved "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz" integrity sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ== -has-color@~0.1.0: - version "0.1.7" - resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" - integrity sha512-kaNz5OTAYYmt646Hkqw50/qyxP2vFnTVu5AQ1Zmk22Kk5+4Qx6BpO8+u7IKsML5fOsFk0ZT0AcCJNYwcvaLBvw== - has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-property-descriptors@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: es-define-property "^1.0.0" html-encoding-sniffer@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" + resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz" integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== dependencies: whatwg-encoding "^3.1.1" http-proxy-agent@^7.0.2: version "7.0.2" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz" integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== dependencies: agent-base "^7.1.0" @@ -1316,7 +1109,7 @@ http-proxy-agent@^7.0.2: https-proxy-agent@^7.0.6: version "7.0.6" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz" integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== dependencies: agent-base "^7.1.2" @@ -1324,41 +1117,64 @@ https-proxy-agent@^7.0.6: husky@^9.1.7: version "9.1.7" - resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d" + resolved "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz" integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== iconv-lite@0.6.3: version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +ignore@^5.2.0: + version "5.3.2" + +import-fresh@^3.2.1: + version "3.3.1" + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + +is-extglob@^2.1.1: + version "2.1.1" + is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-fullwidth-code-point@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz#046b2a6d4f6b156b2233d3207d4b5a9783999b98" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz" integrity sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ== dependencies: get-east-asian-width "^1.3.1" +is-glob@^4.0.0, is-glob@^4.0.3: + version "4.0.3" + dependencies: + is-extglob "^2.1.1" + is-number@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-potential-custom-element-name@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== +isexe@^2.0.0: + version "2.0.0" + jison-lex@0.3.x: version "0.3.4" - resolved "https://registry.yarnpkg.com/jison-lex/-/jison-lex-0.3.4.tgz#81ca28d84f84499dfa8c594dcde3d8a3f26ec7a5" + resolved "https://registry.npmjs.org/jison-lex/-/jison-lex-0.3.4.tgz" integrity sha512-EBh5wrXhls1cUwROd5DcDHR1sG7CdsCFSqY1027+YA1RGxz+BX2TDLAhdsQf40YEtFDGoiO0Qm8PpnBl2EzDJw== dependencies: lex-parser "0.1.x" @@ -1366,26 +1182,29 @@ jison-lex@0.3.x: jison@^0.4.18: version "0.4.18" - resolved "https://registry.yarnpkg.com/jison/-/jison-0.4.18.tgz#c68a6a54bfe7028fa40bcfc6cc8bbd9ed291f502" + resolved "https://registry.npmjs.org/jison/-/jison-0.4.18.tgz" integrity sha512-FKkCiJvozgC7VTHhMJ00a0/IApSxhlGsFIshLW6trWJ8ONX2TQJBBz6DlcO1Gffy4w9LT+uL+PA+CVnUSJMF7w== dependencies: - JSONSelect "0.4.0" cjson "0.3.0" ebnf-parser "0.1.10" escodegen "1.3.x" esprima "1.1.x" jison-lex "0.3.x" + JSONSelect "0.4.0" lex-parser "~0.1.3" nomnom "1.5.2" -jsdom@^27.1.0: - version "27.2.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-27.2.0.tgz#499a41eef477c3632f44009e095cb8e418fdd714" - integrity sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA== +js-yaml@^4.1.0: + version "4.1.0" dependencies: - "@acemir/cssom" "^0.9.23" - "@asamuzakjp/dom-selector" "^6.7.4" - cssstyle "^5.3.3" + argparse "^2.0.1" + +jsdom@*, jsdom@^27.1.0: + version "27.1.0" + dependencies: + "@acemir/cssom" "^0.9.19" + "@asamuzakjp/dom-selector" "^6.7.3" + cssstyle "^5.3.2" data-urls "^6.0.0" decimal.js "^10.6.0" html-encoding-sniffer "^4.0.0" @@ -1404,27 +1223,57 @@ jsdom@^27.1.0: ws "^8.18.3" xml-name-validator "^5.0.0" +json-buffer@3.0.1: + version "3.0.1" + +json-schema-traverse@^0.4.1: + version "0.4.1" + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + json-stringify-safe@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== jsonlint@1.6.0: version "1.6.0" - resolved "https://registry.yarnpkg.com/jsonlint/-/jsonlint-1.6.0.tgz#88aa46bc289a7ac93bb46cae2d58a187a9bb494a" + resolved "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.0.tgz" integrity sha512-x6YLBe6NjdpmIeiklwQOxsZuYj/SOWkT33GlTpaG1UdFGjdWjPcxJ1CWZAX3wA7tarz8E2YHF6KiW5HTapPlXw== dependencies: JSV ">= 4.0.x" nomnom ">= 1.5.x" -lex-parser@0.1.x, lex-parser@~0.1.3: +JSONSelect@0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.4.0.tgz" + integrity sha512-VRLR3Su35MH+XV2lrvh9O7qWoug/TUyj9tLDjn9rtpUCNnILLrHjgd/tB0KrhugCxUpj3UqoLqfYb3fLJdIQQQ== + +"JSV@>= 4.0.x": + version "4.0.2" + resolved "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz" + integrity sha512-ZJ6wx9xaKJ3yFUhq5/sk82PJMuUyLk277I8mQeyDgCTjGdjWJIvPfaU5LIXaMuaN2UO1X3kZH4+lgphublZUHw== + +keyv@^4.5.4: + version "4.5.4" + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lex-parser@~0.1.3, lex-parser@0.1.x: version "0.1.4" - resolved "https://registry.yarnpkg.com/lex-parser/-/lex-parser-0.1.4.tgz#64c4f025f17fd53bfb45763faeb16f015a747550" + resolved "https://registry.npmjs.org/lex-parser/-/lex-parser-0.1.4.tgz" integrity sha512-DuAEISsr1H4LOpmFLkyMc8YStiRWZCO8hMsoXAXSbgyfvs2WQhSt0+/FBv3ZU/JBFZMGcE+FWzEBSzwUU7U27w== lint-staged@^16.2.7: version "16.2.7" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-16.2.7.tgz#c4a635960c17b52fe774f1f40aee8ce1bd86531f" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz" integrity sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow== dependencies: commander "^14.0.2" @@ -1437,7 +1286,7 @@ lint-staged@^16.2.7: listr2@^9.0.5: version "9.0.5" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-9.0.5.tgz#92df7c4416a6da630eb9ef46da469b70de97b316" + resolved "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz" integrity sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g== dependencies: cli-truncate "^5.0.0" @@ -1447,14 +1296,22 @@ listr2@^9.0.5: rfdc "^1.4.1" wrap-ansi "^9.0.0" +locate-path@^6.0.0: + version "6.0.0" + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + lodash@^4.17.21: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-update@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.1.0.tgz#1a04ff38166f94647ae1af562f4bd6a15b1b7cd4" + resolved "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz" integrity sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== dependencies: ansi-escapes "^7.0.0" @@ -1465,36 +1322,34 @@ log-update@^6.1.0: long@^5.0.0, long@^5.2.3: version "5.3.2" - resolved "https://registry.yarnpkg.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83" + resolved "https://registry.npmjs.org/long/-/long-5.3.2.tgz" integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== -lru-cache@^11.2.2: +lru-cache@^11.2.1, lru-cache@^11.2.2: version "11.2.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.2.tgz#40fd37edffcfae4b2940379c0722dc6eeaa75f24" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz" integrity sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg== -magic-string@^0.30.21: +magic-string@^0.30.19: version "0.30.21" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91" - integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== dependencies: "@jridgewell/sourcemap-codec" "^1.5.5" matcher@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" + resolved "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz" integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== dependencies: escape-string-regexp "^4.0.0" mdn-data@2.12.2: version "2.12.2" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.12.2.tgz#9ae6c41a9e65adf61318b32bff7b64fbfb13f8cf" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz" integrity sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA== micromatch@^4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: braces "^3.0.3" @@ -1502,70 +1357,70 @@ micromatch@^4.0.8: mimic-function@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" + resolved "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz" integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== +minimatch@^3.1.2: + version "3.1.2" + dependencies: + brace-expansion "^1.1.7" + mrmime@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.1.tgz#bc3e87f7987853a54c9850eeb1f1078cd44adddc" + resolved "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz" integrity sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ== ms@^2.1.3: version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== muggle-string@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/muggle-string/-/muggle-string-0.4.1.tgz#3b366bd43b32f809dc20659534dd30e7c8a0d328" + resolved "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz" integrity sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ== nano-spawn@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/nano-spawn/-/nano-spawn-2.0.0.tgz#f1250434c09ae18870d4f729fc54b406cf85a3e1" + resolved "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz" integrity sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw== nanoid@^3.3.11: version "3.3.11" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz" integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== -nomnom@1.5.2: +natural-compare@^1.4.0: + version "1.4.0" + +"nomnom@>= 1.5.x", nomnom@1.5.2: version "1.5.2" - resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.5.2.tgz#f4345448a853cfbd5c0d26320f2477ab0526fe2f" + resolved "https://registry.npmjs.org/nomnom/-/nomnom-1.5.2.tgz" integrity sha512-fiVbT7BqxiQqjlR9U3FDGOSERFCKoXVCdxV2FwZuNN7/cmJ42iQx35nUFOAFDcyvemu9Adp+IlsCGlKQYLmBKw== dependencies: colors "0.5.x" underscore "1.1.x" -"nomnom@>= 1.5.x": - version "1.8.1" - resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7" - integrity sha512-5s0JxqhDx9/rksG2BTMVN1enjWSvPidpoSgViZU4ZXULyTe+7jxcCRLB6f42Z0l1xYJpleCBtSyY6Lwg3uu5CQ== - dependencies: - chalk "~0.4.0" - underscore "~1.6.0" - object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== onetime@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" + resolved "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz" integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== dependencies: mimic-function "^5.0.0" onnxruntime-common@1.23.2: version "1.23.2" - resolved "https://registry.yarnpkg.com/onnxruntime-common/-/onnxruntime-common-1.23.2.tgz#669892f8d2b7de273ac60a76151781a97d01ab49" + resolved "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.23.2.tgz" integrity sha512-5LFsC9Dukzp2WV6kNHYLNzp8sT6V02IubLCbzw2Xd6X5GOlr65gAX6xiJwyi2URJol/s71gaQLC5F2C25AAR2w== -onnxruntime-node@1.23.2: +onnxruntime-node@^1.23.2: version "1.23.2" - resolved "https://registry.yarnpkg.com/onnxruntime-node/-/onnxruntime-node-1.23.2.tgz#6e205a32111bf6657ac469f8fc7fb35b765324a5" + resolved "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.23.2.tgz" integrity sha512-OBTsG0W8ddBVOeVVVychpVBS87A9YV5sa2hJ6lc025T97Le+J4v++PwSC4XFs1C62SWyNdof0Mh4KvnZgtt4aw== dependencies: adm-zip "^0.5.16" @@ -1574,7 +1429,7 @@ onnxruntime-node@1.23.2: onnxruntime-web@1.23.2: version "1.23.2" - resolved "https://registry.yarnpkg.com/onnxruntime-web/-/onnxruntime-web-1.23.2.tgz#1d7883e7bc1d717f85d75ca7d564b3129feed51b" + resolved "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.23.2.tgz" integrity sha512-T09JUtMn+CZLk3mFwqiH0lgQf+4S7+oYHHtk6uhaYAAJI95bTcKi5bOOZYwORXfS/RLZCjDDEXGWIuOCAFlEjg== dependencies: flatbuffers "^25.1.24" @@ -1584,72 +1439,106 @@ onnxruntime-web@1.23.2: platform "^1.3.6" protobufjs "^7.2.4" +optionator@^0.9.3: + version "0.9.4" + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^3.0.2: + version "3.1.0" + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + dependencies: + p-limit "^3.0.2" + +parent-module@^1.0.0: + version "1.0.1" + dependencies: + callsites "^3.0.0" + parse5@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-8.0.0.tgz#aceb267f6b15f9b6e6ba9e35bfdd481fc2167b12" + resolved "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz" integrity sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA== dependencies: entities "^6.0.0" path-browserify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz" integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== +path-exists@^4.0.0: + version "4.0.0" + +path-key@^3.1.0: + version "3.1.1" + pathe@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + resolved "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz" integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== picocolors@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -picomatch@^4.0.2, picomatch@^4.0.3: +"picomatch@^3 || ^4", picomatch@^4.0.2, picomatch@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz" integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== pidtree@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz" integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== platform@^1.3.6: version "1.3.6" - resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" + resolved "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz" integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== postcss@^8.4.43, postcss@^8.5.6: version "8.5.6" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz" integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== dependencies: nanoid "^3.3.11" picocolors "^1.1.1" source-map-js "^1.2.1" +prelude-ls@^1.2.1: + version "1.2.1" + prettier-linter-helpers@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz" integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== dependencies: fast-diff "^1.1.2" -prettier@^3.6.2: +prettier@^3.6.2, prettier@>=3.0.0: version "3.6.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393" + resolved "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz" integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== protobufjs@^7.2.4: version "7.5.4" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.4.tgz#885d31fe9c4b37f25d1bb600da30b1c5b37d286a" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz" integrity sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg== dependencies: "@protobufjs/aspromise" "^1.1.2" @@ -1665,29 +1554,32 @@ protobufjs@^7.2.4: "@types/node" ">=13.7.0" long "^5.0.0" -punycode@^2.3.1: +punycode@^2.1.0, punycode@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-from-string@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +resolve-from@^4.0.0: + version "4.0.0" + resolve-pkg-maps@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + resolved "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== restore-cursor@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-5.1.0.tgz#0766d95699efacb14150993f55baf0953ea1ebe7" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz" integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== dependencies: onetime "^7.0.0" @@ -1695,12 +1587,12 @@ restore-cursor@^5.0.0: rfdc@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz" integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== roarr@^2.15.3: version "2.15.4" - resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" + resolved "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz" integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== dependencies: boolean "^3.0.1" @@ -1711,90 +1603,96 @@ roarr@^2.15.3: sprintf-js "^1.1.2" rollup@^4.20.0, rollup@^4.43.0: - version "4.53.3" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.53.3.tgz#dbc8cd8743b38710019fb8297e8d7a76e3faa406" - integrity sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA== + version "4.52.5" dependencies: "@types/estree" "1.0.8" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.53.3" - "@rollup/rollup-android-arm64" "4.53.3" - "@rollup/rollup-darwin-arm64" "4.53.3" - "@rollup/rollup-darwin-x64" "4.53.3" - "@rollup/rollup-freebsd-arm64" "4.53.3" - "@rollup/rollup-freebsd-x64" "4.53.3" - "@rollup/rollup-linux-arm-gnueabihf" "4.53.3" - "@rollup/rollup-linux-arm-musleabihf" "4.53.3" - "@rollup/rollup-linux-arm64-gnu" "4.53.3" - "@rollup/rollup-linux-arm64-musl" "4.53.3" - "@rollup/rollup-linux-loong64-gnu" "4.53.3" - "@rollup/rollup-linux-ppc64-gnu" "4.53.3" - "@rollup/rollup-linux-riscv64-gnu" "4.53.3" - "@rollup/rollup-linux-riscv64-musl" "4.53.3" - "@rollup/rollup-linux-s390x-gnu" "4.53.3" - "@rollup/rollup-linux-x64-gnu" "4.53.3" - "@rollup/rollup-linux-x64-musl" "4.53.3" - "@rollup/rollup-openharmony-arm64" "4.53.3" - "@rollup/rollup-win32-arm64-msvc" "4.53.3" - "@rollup/rollup-win32-ia32-msvc" "4.53.3" - "@rollup/rollup-win32-x64-gnu" "4.53.3" - "@rollup/rollup-win32-x64-msvc" "4.53.3" + "@rollup/rollup-android-arm-eabi" "4.52.5" + "@rollup/rollup-android-arm64" "4.52.5" + "@rollup/rollup-darwin-arm64" "4.52.5" + "@rollup/rollup-darwin-x64" "4.52.5" + "@rollup/rollup-freebsd-arm64" "4.52.5" + "@rollup/rollup-freebsd-x64" "4.52.5" + "@rollup/rollup-linux-arm-gnueabihf" "4.52.5" + "@rollup/rollup-linux-arm-musleabihf" "4.52.5" + "@rollup/rollup-linux-arm64-gnu" "4.52.5" + "@rollup/rollup-linux-arm64-musl" "4.52.5" + "@rollup/rollup-linux-loong64-gnu" "4.52.5" + "@rollup/rollup-linux-ppc64-gnu" "4.52.5" + "@rollup/rollup-linux-riscv64-gnu" "4.52.5" + "@rollup/rollup-linux-riscv64-musl" "4.52.5" + "@rollup/rollup-linux-s390x-gnu" "4.52.5" + "@rollup/rollup-linux-x64-gnu" "4.52.5" + "@rollup/rollup-linux-x64-musl" "4.52.5" + "@rollup/rollup-openharmony-arm64" "4.52.5" + "@rollup/rollup-win32-arm64-msvc" "4.52.5" + "@rollup/rollup-win32-ia32-msvc" "4.52.5" + "@rollup/rollup-win32-x64-gnu" "4.52.5" + "@rollup/rollup-win32-x64-msvc" "4.52.5" fsevents "~2.3.2" rxjs@^7.0.0: version "7.8.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz" integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== dependencies: tslib "^2.1.0" "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== saxes@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + resolved "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz" integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== dependencies: xmlchars "^2.2.0" semver-compare@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + resolved "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz" integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== semver@^7.3.2: version "7.7.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" + resolved "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== serialize-error@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" + resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz" integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== dependencies: type-fest "^0.13.1" +shebang-command@^2.0.0: + version "2.0.0" + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + shell-quote@^1.7.3: version "1.8.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.3.tgz#55e40ef33cf5c689902353a3d8cd1a6725f08b4b" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz" integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== siginfo@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + resolved "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz" integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== signal-exit@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== sirv@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-3.0.2.tgz#f775fccf10e22a40832684848d636346f41cd970" + resolved "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz" integrity sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g== dependencies: "@polka/url" "^1.0.0-next.24" @@ -1803,7 +1701,7 @@ sirv@^3.0.2: slice-ansi@^7.1.0: version "7.1.2" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.2.tgz#adf7be70aa6d72162d907cd0e6d5c11f507b5403" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz" integrity sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w== dependencies: ansi-styles "^6.2.1" @@ -1811,44 +1709,42 @@ slice-ansi@^7.1.0: source-map-js@^1.0.1, source-map-js@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== source-map@~0.1.33: version "0.1.43" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz" integrity sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ== dependencies: amdefine ">=0.0.4" spawn-command@^0.0.2-1: version "0.0.2" - resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e" + resolved "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz" integrity sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ== sprintf-js@^1.1.2: version "1.1.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz" integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== stackback@0.0.2: version "0.0.2" - resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + resolved "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz" integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== -std-env@^3.10.0: +std-env@^3.9.0: version "3.10.0" - resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.10.0.tgz#d810b27e3a073047b2b5e40034881f5ea6f9c83b" - integrity sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg== string-argv@^0.3.2: version "0.3.2" - resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz" integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -1857,7 +1753,7 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: string-width@^7.0.0, string-width@^7.2.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + resolved "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz" integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== dependencies: emoji-regex "^10.3.0" @@ -1866,7 +1762,7 @@ string-width@^7.0.0, string-width@^7.2.0: string-width@^8.0.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.1.0.tgz#9e9fb305174947cf45c30529414b5da916e9e8d1" + resolved "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz" integrity sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg== dependencies: get-east-asian-width "^1.3.0" @@ -1874,62 +1770,60 @@ string-width@^8.0.0: strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-ansi@^7.1.0: version "7.1.2" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz" integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== dependencies: ansi-regex "^6.0.1" -strip-ansi@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991" - integrity sha512-behete+3uqxecWlDAm5lmskaSaISA+ThQ4oNNBDTBJt0x2ppR6IPqfZNuj6BLaLJ/Sji4TPZlcRyOis8wXQTLg== +strip-json-comments@^3.1.1: + version "3.1.1" supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-color@^8.1.0: version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" symbol-tree@^3.2.4: version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== synckit@^0.11.7: version "0.11.11" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.11.tgz#c0b619cf258a97faa209155d9cd1699b5c998cb0" + resolved "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz" integrity sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw== dependencies: "@pkgr/core" "^0.2.9" tinybench@^2.9.0: version "2.9.0" - resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + resolved "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz" integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== tinyexec@^0.3.2: version "0.3.2" - resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz" integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== tinyglobby@^0.2.15: version "0.2.15" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz" integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== dependencies: fdir "^6.5.0" @@ -1937,60 +1831,56 @@ tinyglobby@^0.2.15: tinyrainbow@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-3.0.3.tgz#984a5b1c1b25854a9b6bccbe77964d0593d1ea42" + resolved "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz" integrity sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q== -tldts-core@^7.0.18: - version "7.0.18" - resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.0.18.tgz#78edfd38e8c35e20fb4d2cde63c759139e169d31" - integrity sha512-jqJC13oP4FFAahv4JT/0WTDrCF9Okv7lpKtOZUGPLiAnNbACcSg8Y8T+Z9xthOmRBqi/Sob4yi0TE0miRCvF7Q== +tldts-core@^7.0.17: + version "7.0.17" tldts@^7.0.5: - version "7.0.18" - resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.0.18.tgz#72cac7a2bdb6bba78f8a09fdf7ef84843b09aa94" - integrity sha512-lCcgTAgMxQ1JKOWrVGo6E69Ukbnx4Gc1wiYLRf6J5NN4HRYJtCby1rPF8rkQ4a6qqoFBK5dvjJ1zJ0F7VfDSvw== + version "7.0.17" dependencies: - tldts-core "^7.0.18" + tldts-core "^7.0.17" to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" totalist@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + resolved "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz" integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== tough-cookie@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-6.0.0.tgz#11e418b7864a2c0d874702bc8ce0f011261940e5" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz" integrity sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w== dependencies: tldts "^7.0.5" tr46@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-6.0.0.tgz#f5a1ae546a0adb32a277a2278d0d17fa2f9093e6" + resolved "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz" integrity sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw== dependencies: punycode "^2.3.1" tree-kill@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== tslib@^2.1.0: version "2.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== -tsx@^4.20.6: +tsx@^4.20.6, tsx@^4.8.1: version "4.20.6" - resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.20.6.tgz#8fb803fd9c1f70e8ccc93b5d7c5e03c3979ccb2e" + resolved "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz" integrity sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg== dependencies: esbuild "~0.25.0" @@ -1998,34 +1888,39 @@ tsx@^4.20.6: optionalDependencies: fsevents "~2.3.3" +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + dependencies: + prelude-ls "^1.2.1" + type-fest@^0.13.1: version "0.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz" integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== -typescript@^5.2.2: +typescript@*, typescript@^5.2.2, typescript@>=5.0.0: version "5.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== underscore@1.1.x: version "1.1.7" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.1.7.tgz#40bab84bad19d230096e8d6ef628bff055d83db0" + resolved "https://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz" integrity sha512-w4QtCHoLBXw1mjofIDoMyexaEdWGMedWNDhlWTtT1V1lCRqi65Pnoygkh6+WRdr+Bm8ldkBNkNeCsXGMlQS9HQ== -underscore@~1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" - integrity sha512-z4o1fvKUojIWh9XuaVLUDdf86RQiq13AC1dmHbTpoyuu+bquHms76v16CjycCbec87J7z0k//SiQVk0sMdFmpQ== - undici-types@~7.16.0: version "7.16.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz" integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== -vite@^5.4.21: +uri-js@^4.2.2: + version "4.4.1" + dependencies: + punycode "^2.1.0" + +"vite@^5.0.0 || ^6.0.0", vite@^5.4.21: version "5.4.21" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.21.tgz#84a4f7c5d860b071676d39ba513c0d598fdc7027" + resolved "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz" integrity sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw== dependencies: esbuild "^0.21.3" @@ -2034,10 +1929,8 @@ vite@^5.4.21: optionalDependencies: fsevents "~2.3.3" -"vite@^6.0.0 || ^7.0.0": - version "7.2.4" - resolved "https://registry.yarnpkg.com/vite/-/vite-7.2.4.tgz#a3a09c7e25487612ecc1119c7d412c73da35bd4e" - integrity sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w== +"vite@^6.0.0 || ^7.0.0", "vite@^6.0.0 || ^7.0.0-0": + version "7.1.12" dependencies: esbuild "^0.25.0" fdir "^6.5.0" @@ -2048,25 +1941,23 @@ vite@^5.4.21: optionalDependencies: fsevents "~2.3.3" -vitest@^4.0.6: - version "4.0.12" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-4.0.12.tgz#68344eab5b78427ecb5e0abac521fc05c08315c0" - integrity sha512-pmW4GCKQ8t5Ko1jYjC3SqOr7TUKN7uHOHB/XGsAIb69eYu6d1ionGSsb5H9chmPf+WeXt0VE7jTXsB1IvWoNbw== - dependencies: - "@vitest/expect" "4.0.12" - "@vitest/mocker" "4.0.12" - "@vitest/pretty-format" "4.0.12" - "@vitest/runner" "4.0.12" - "@vitest/snapshot" "4.0.12" - "@vitest/spy" "4.0.12" - "@vitest/utils" "4.0.12" +vitest@^4.0.6, vitest@4.0.6: + version "4.0.6" + dependencies: + "@vitest/expect" "4.0.6" + "@vitest/mocker" "4.0.6" + "@vitest/pretty-format" "4.0.6" + "@vitest/runner" "4.0.6" + "@vitest/snapshot" "4.0.6" + "@vitest/spy" "4.0.6" + "@vitest/utils" "4.0.6" debug "^4.4.3" es-module-lexer "^1.7.0" expect-type "^1.2.2" - magic-string "^0.30.21" + magic-string "^0.30.19" pathe "^2.0.3" picomatch "^4.0.3" - std-env "^3.10.0" + std-env "^3.9.0" tinybench "^2.9.0" tinyexec "^0.3.2" tinyglobby "^0.2.15" @@ -2076,71 +1967,75 @@ vitest@^4.0.6: vscode-uri@^3.0.8: version "3.1.0" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c" + resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz" integrity sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ== vue-tsc@^3.1.3: - version "3.1.4" - resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-3.1.4.tgz#a2a4d2123a1089e7c0a63c872ddb39f6119eb232" - integrity sha512-GsRJxttj4WkmXW/zDwYPGMJAN3np/4jTzoDFQTpTsI5Vg/JKMWamBwamlmLihgSVHO66y9P7GX+uoliYxeI4Hw== + version "3.1.3" dependencies: "@volar/typescript" "2.4.23" - "@vue/language-core" "3.1.4" + "@vue/language-core" "3.1.3" -vue@^3.3.4: - version "3.5.24" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.24.tgz#1b93af349faa064f46e70ac5b0caaa3d0952bbf3" - integrity sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg== +vue@^3.2.25, vue@^3.3.4, vue@3.5.22: + version "3.5.22" dependencies: - "@vue/compiler-dom" "3.5.24" - "@vue/compiler-sfc" "3.5.24" - "@vue/runtime-dom" "3.5.24" - "@vue/server-renderer" "3.5.24" - "@vue/shared" "3.5.24" + "@vue/compiler-dom" "3.5.22" + "@vue/compiler-sfc" "3.5.22" + "@vue/runtime-dom" "3.5.22" + "@vue/server-renderer" "3.5.22" + "@vue/shared" "3.5.22" w3c-xmlserializer@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" + resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz" integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== dependencies: xml-name-validator "^5.0.0" webidl-conversions@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-8.0.0.tgz#821c92aa4f88d88a31264d887e244cb9655690c6" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.0.tgz" integrity sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA== whatwg-encoding@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz" integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== dependencies: iconv-lite "0.6.3" whatwg-mimetype@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz" integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== whatwg-url@^15.0.0, whatwg-url@^15.1.0: version "15.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-15.1.0.tgz#5c433439b9a5789eeb3806bbd0da89a8bd40b8d7" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz" integrity sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g== dependencies: tr46 "^6.0.0" webidl-conversions "^8.0.0" +which@^2.0.1: + version "2.0.2" + dependencies: + isexe "^2.0.0" + why-is-node-running@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + resolved "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz" integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== dependencies: siginfo "^2.0.0" stackback "0.0.2" +word-wrap@^1.2.5: + version "1.2.5" + wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -2149,7 +2044,7 @@ wrap-ansi@^7.0.0: wrap-ansi@^9.0.0: version "9.0.2" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz#956832dea9494306e6d209eb871643bb873d7c98" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz" integrity sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww== dependencies: ansi-styles "^6.2.1" @@ -2158,42 +2053,42 @@ wrap-ansi@^9.0.0: ws@^8.18.3: version "8.18.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + resolved "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== xml-name-validator@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz" integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== xmlchars@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yaml@^2.8.1: +yaml@^2.4.2, yaml@^2.8.1: version "2.8.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.1.tgz#1870aa02b631f7e8328b93f8bc574fac5d6c4d79" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz" integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw== yargs-parser@^21.1.1: version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs-parser@^22.0.0: version "22.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-22.0.0.tgz#87b82094051b0567717346ecd00fd14804b357c8" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz" integrity sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw== yargs@^17.3.1: version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" @@ -2206,7 +2101,7 @@ yargs@^17.3.1: yargs@^18.0.0: version "18.0.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-18.0.0.tgz#6c84259806273a746b09f579087b68a3c2d25bd1" + resolved "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz" integrity sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg== dependencies: cliui "^9.0.1" @@ -2215,3 +2110,6 @@ yargs@^18.0.0: string-width "^7.2.0" y18n "^5.0.5" yargs-parser "^22.0.0" + +yocto-queue@^0.1.0: + version "0.1.0"