cm-engine-runner 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/favicon.ico ADDED
Binary file
package/index.html ADDED
@@ -0,0 +1,36 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport"
6
+ content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
7
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
8
+ <title>cm-engine-runner</title>
9
+ </head>
10
+ <body>
11
+ <h1>cm-engine-runner</h1>
12
+ <p>Logs are in the developer console</p>
13
+ <script type="module" crossorigin="anonymous">
14
+ import {PolyglotRunner} from "./src/cm-engine-runner/PolyglotRunner.js"
15
+ import {StockfishRunner} from "./src/cm-engine-runner/StockfishRunner.js"
16
+
17
+ const polyglotRunner = new PolyglotRunner({bookUrl: "./assets/books/openings.bin", responseDelay: 0, debug: true})
18
+ const startingPosition = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
19
+ const startingFewMoves = "rnbqkbnr/pppppp2/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 1"
20
+ console.log("polyglot", startingPosition, await polyglotRunner.calculateMove(startingPosition))
21
+ console.log("polyglot", startingFewMoves, await polyglotRunner.calculateMove(startingFewMoves))
22
+
23
+ const positionEndGame = "8/8/8/2k5/4K3/8/8/8"
24
+ console.log("polyglot", positionEndGame, await polyglotRunner.calculateMove(positionEndGame))
25
+
26
+ const stockfishRunner = new StockfishRunner({workerUrl: "./engines/stockfish-v10-niklasf.js", responseDelay: 0, debug: true})
27
+ console.log("stockfish", await stockfishRunner.calculateMove(startingFewMoves))
28
+ const postitionBlackWillWin = "4k3/3r4/8/8/8/8/6K1/8 w - - 0 1"
29
+ console.log("stockfish", await stockfishRunner.calculateMove(postitionBlackWillWin, {level: 1}))
30
+ console.log("stockfish", await stockfishRunner.calculateMove(postitionBlackWillWin, {level: 10}))
31
+
32
+ const positionManyPawns = "ppppkppp/pppppppp/pppppppp/pppppppp/8/5N2/8/RNBQKB1R b KQ - 0 1"
33
+ console.log("stockfish", await stockfishRunner.calculateMove(positionManyPawns, {level: 3}))
34
+ </script>
35
+ </body>
36
+ </html>
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "cm-engine-runner",
3
+ "description": "abstraction layer to run chess engines, supports opening books",
4
+ "version": "1.0.11",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1",
8
+ "postinstall": "node postinstall.js"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/shaack/cm-chess-engine-adapter.git"
13
+ },
14
+ "keywords": [
15
+ "chess",
16
+ "engine",
17
+ "chessmail",
18
+ "es6"
19
+ ],
20
+ "author": "shaack.com",
21
+ "license": "MIT",
22
+ "bugs": {
23
+ "url": "https://github.com/shaack/cm-chess-engine-adapter/issues"
24
+ },
25
+ "homepage": "https://github.com/shaack/cm-chess-engine-adapter#readme",
26
+ "devDependencies": {
27
+ "teevi": "^2.1.10"
28
+ },
29
+ "dependencies": {
30
+ "cm-polyglot": "^1.0.6",
31
+ "modrator": "^1.2.1"
32
+ }
33
+ }
package/postinstall.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Author and copyright: Stefan Haack (https://shaack.com)
3
+ * License: MIT, see file 'LICENSE'
4
+ */
5
+
6
+ const ModRator = require("modrator/src/ModRator.js")
7
+ const modRator = new ModRator(__dirname)
8
+
9
+ modRator.addToLibrary("cm-polyglot")
10
+ modRator.addToLibrary("cm-polyglot", "src", "stakelbase")
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Author and copyright: Stefan Haack (https://shaack.com)
3
+ * Repository: https://github.com/shaack/cm-engine-runner
4
+ * License: MIT, see file 'LICENSE'
5
+ */
6
+
7
+ export const ENGINE_STATE = {
8
+ LOADING: 1,
9
+ LOADED: 2,
10
+ READY: 3,
11
+ THINKING: 4
12
+ }
13
+
14
+ export class EngineRunner {
15
+
16
+ constructor(props) {
17
+ this.props = {
18
+ debug: false,
19
+ responseDelay: 1000 // https://www.reddit.com/r/ProgrammerHumor/comments/6xwely/from_the_apple_chess_engine_code/
20
+ // https://opensource.apple.com/source/Chess/Chess-347/Sources/MBCEngine.mm.auto.html
21
+ }
22
+ Object.assign(this.props, props)
23
+ this.engineState = ENGINE_STATE.LOADING
24
+ this.initialization = this.init()
25
+ }
26
+
27
+ init() {
28
+ return Promise.reject("you have to implement `init()` in the EngineRunner")
29
+ }
30
+
31
+ calculateMove(fen, props = {}) {
32
+
33
+ }
34
+
35
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Author and copyright: Stefan Haack (https://shaack.com)
3
+ * Repository: https://github.com/shaack/cm-engine-runner
4
+ * License: MIT, see file 'LICENSE'
5
+ */
6
+ import {ENGINE_STATE, EngineRunner} from "./EngineRunner.js"
7
+ import {Polyglot} from "../../lib/cm-polyglot/Polyglot.js"
8
+
9
+ export class PolyglotRunner extends EngineRunner {
10
+
11
+ constructor(props) {
12
+ super(props)
13
+ }
14
+
15
+ init() {
16
+ this.polyglot = new Polyglot(this.props.bookUrl)
17
+ this.polyglot.initialisation.then(() => {
18
+ this.engineState = ENGINE_STATE.READY
19
+ return Promise.resolve()
20
+ })
21
+ }
22
+
23
+ calculateMove(fen, props = {}) {
24
+ this.engineState = ENGINE_STATE.THINKING
25
+ const timeoutPromise = new Promise((resolve) => {
26
+ setTimeout(async () => {
27
+ resolve()
28
+ }, this.props.responseDelay)
29
+ })
30
+ const calculationPromise = new Promise(async (resolve) => {
31
+ const moves = await this.polyglot.getMovesFromFen(fen)
32
+ if(this.props.debug) {
33
+ console.log(fen, "moves found in opening book", moves)
34
+ }
35
+ // handle propability
36
+ const propabilityMatrix = []
37
+ for (const move of moves) {
38
+ for (let i = 0; i < (move.probability * 10); i++) {
39
+ propabilityMatrix.push(move)
40
+ }
41
+ }
42
+ // propability weighted random
43
+ const luckyIndex = Math.floor(Math.random() * propabilityMatrix.length)
44
+ resolve(propabilityMatrix[luckyIndex])
45
+ })
46
+ return new Promise((resolve) => {
47
+ Promise.all([this.initialization, timeoutPromise, calculationPromise]).then((values) => {
48
+ this.engineState = ENGINE_STATE.READY
49
+ resolve(values[2])
50
+ })
51
+ })
52
+ }
53
+
54
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Author and copyright: Stefan Haack (https://shaack.com)
3
+ * Repository: https://github.com/shaack/cm-engine-runner
4
+ * License: MIT, see file 'LICENSE'
5
+ */
6
+
7
+ import {ENGINE_STATE, EngineRunner} from "./EngineRunner.js"
8
+
9
+ const LEVEL_DEPTH = {
10
+ 1: 1,
11
+ 2: 2,
12
+ 3: 3,
13
+ 4: 4,
14
+ 5: 7,
15
+ 6: 10,
16
+ 7: 13,
17
+ 8: 16,
18
+ 9: 19,
19
+ 10: 22
20
+ }
21
+
22
+ export class StockfishRunner extends EngineRunner {
23
+
24
+ constructor(props) {
25
+ super(props)
26
+ }
27
+
28
+ init() {
29
+ return new Promise((resolve) => {
30
+ const listener = (event) => {
31
+ this.workerListener(event)
32
+ }
33
+ if (this.engineWorker) {
34
+ this.engineWorker.removeEventListener("message", listener)
35
+ this.engineWorker.terminate()
36
+ }
37
+ this.engineWorker = new Worker(this.props.workerUrl)
38
+ this.engineWorker.addEventListener("message", listener)
39
+
40
+ this.uciCmd('uci')
41
+ this.uciCmd('ucinewgame')
42
+ this.uciCmd('isready')
43
+ resolve()
44
+ })
45
+ }
46
+
47
+ workerListener(event) {
48
+ if(this.props.debug) {
49
+ if(event.type === "message") {
50
+ console.log(" msg", event.data)
51
+ } else {
52
+ console.log(event)
53
+ }
54
+ }
55
+ const line = event.data
56
+ if (line === 'uciok') {
57
+ this.engineState = ENGINE_STATE.LOADED
58
+ } else if (line === 'readyok') {
59
+ this.engineState = ENGINE_STATE.READY
60
+ } else {
61
+ let match = line.match(/^info .*\bscore (\w+) (-?\d+)/)
62
+ if (match) {
63
+ const score = parseInt(match[2], 10)
64
+ let tmpScore
65
+ if (match[1] === 'cp') {
66
+ tmpScore = (score / 100.0).toFixed(1)
67
+ } else if (match[1] === 'mate') {
68
+ tmpScore = '#' + Math.abs(score)
69
+ }
70
+ this.score = tmpScore
71
+ }
72
+ // match = line.match(/^bestmove ([a-h][1-8])([a-h][1-8])([qrbk])?/) // ponder is not always included
73
+ match = line.match(/^bestmove ([a-h][1-8])([a-h][1-8])([qrbk])?( ponder ([a-h][1-8])?([a-h][1-8])?)?/)
74
+ if (match) {
75
+ this.engineState = ENGINE_STATE.READY
76
+ if (match[4] !== undefined) {
77
+ this.ponder = {from: match[5], to: match[6]}
78
+ } else {
79
+ this.ponder = undefined
80
+ }
81
+ const move = {from: match[1], to: match[2], promotion: match[3], score: this.score, ponder: this.ponder}
82
+ this.moveResponse(move)
83
+ } else {
84
+ match = line.match(/^info .*\bdepth (\d+) .*\bnps (\d+)/)
85
+ if (match) {
86
+ this.engineState = ENGINE_STATE.THINKING
87
+ this.search = 'Depth: ' + match[1] + ' Nps: ' + match[2]
88
+ }
89
+ }
90
+ }
91
+ }
92
+
93
+ calculateMove(fen, props = { level: 4 }) {
94
+ this.engineState = ENGINE_STATE.THINKING
95
+ const timeoutPromise = new Promise((resolve) => {
96
+ setTimeout(async () => {
97
+ resolve()
98
+ }, this.props.responseDelay)
99
+ })
100
+ const calculationPromise = new Promise ((resolve) => {
101
+ setTimeout(() => {
102
+ this.uciCmd('position fen ' + fen)
103
+ this.uciCmd('go depth ' + (LEVEL_DEPTH[props.level]))
104
+ this.moveResponse = (move) => {
105
+ resolve(move)
106
+ }
107
+ }, this.props.responseDelay)
108
+ })
109
+ return new Promise((resolve) => {
110
+ Promise.all([this.initialisation, timeoutPromise, calculationPromise]).then((values) => {
111
+ this.engineState = ENGINE_STATE.READY
112
+ resolve(values[2])
113
+ })
114
+ })
115
+ }
116
+
117
+ uciCmd(cmd) {
118
+ if(this.props.debug) {
119
+ console.log(" uci ->", cmd)
120
+ }
121
+ this.engineWorker.postMessage(cmd)
122
+ }
123
+
124
+ }