I have already some elements for my answer, coming from an old post (Trying to implement inline Webworker for a recursive function).
Now I take over this little code implying this issue. This little code uses inline webworkers since I use a recursive function which computes the best hit to play (this is the "computer" Hit). If I don't use webworker and I take a depth too high, the game is hanging in browser.
the source of code is available on [this link][1]
I would like to get a working code. Indeed, I am near to the solution since several bugs have been fixed (for example, the fact to include different functions in webworker to make it do its computations without calling external functions. If I don't include all necessary functions, the webworker will not be able to work since it has its own scope.
So I would like to get help to debug my current version.
The game is available on [this link][2] I am interested only into "computer vs player" mode (black begins to play). Here below you can find the part when I call the recursive function.
Once computation is done with this recursive function, I would like to get back the object representing the current map of game (positions of black/white circles).
To get back the object or the coordinates suggested for hit (Here we are in main function), I did the following code :
// Call the recursive function and get final (a,b) results
new Promise( resolve => {
let HitTemp = JSON.parse(JSON.stringify(HitCurrent));
let firstWorker = new Worker( workerScript );
firstWorker.onmessage = function ( event ) {
resolve( event.data ); //{ result: XXX }
console.log('here5');
}
firstWorker.postMessage([HitTemp, HitTemp.playerCurrent, maxNodes]);
} ).then( ( { result } ) => {
//let [ a, b ] = result.coordPlayable;
let [ a, b ] = HitTemp.coordPlayable;
console.log('result3 : ', result);
console.log('here3 : ', a, b);
} );
// HERE I TRY TO USE a and b in exploreHitLine function
// who needs the variables a and b BUT I DON'T KNOW
// IF a AND b ARE KNOWN HERE FROM CODE ABOVE
for (k = 0; k < 8; k++) {
exploreHitLine(HitCurrent, a, b, k, 'drawing');
}
and below the recursive function (inside inline webworker part) :
window.onload = function() {
// Inline webworker version
workerScript = URL.createObjectURL( new Blob( [ `
"use strict";
...
...
// All other variables and functions necessary for inline webworker
...
...
...
function negaMax(HitCurrent, colorCurrent, depth) {
// Indices
var i, j, k;
// Evaluation
var arrayTemp, evalFinal, e;
// Set current color to HitCurrent
HitCurrent.playerCurrent = colorCurrent;
// Deep copy of arrayCurrent array
arrayTemp = JSON.parse(JSON.stringify(HitCurrent.arrayCurrent));
// If depth equal to 0
if (depth == 0)
return evaluation(HitCurrent);
// Starting evaluation
evalFinal = -infinity;
// Compute all playable hits and check if playable hits
if (computeHit(HitCurrent, 'playable')) {
// Browse all possible hits
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
if (HitCurrent.arrayPlayable[i][j] == 'playable') {
for (k = 0; k < 8; k++) {
// Explore line started from (i,j) with direction "k"
exploreHitLine(HitCurrent, i, j, k, 'drawing');
}
// Recursive call
e = -negaMax(JSON.parse(JSON.stringify(HitCurrent)), ((JSON.stringify(HitCurrent.playerCurrent) == JSON.stringify(playerBlack)) ? playerWhite : playerBlack), depth-1);
if (e > evalFinal) {
HitCurrent.coordPlayable = [i,j];
evalFinal = e;
}
if (e == -infinity) {
HitCurrent.coordPlayable = [i,j];
}
// Restore arrayCurrent array
HitCurrent.arrayCurrent = JSON.parse(JSON.stringify(arrayTemp));
}
// Clean playable hits once double loop is done
cleanHits('playable', HitCurrent);
}
console.log('here2 :', evalFinal);
return evalFinal;
}
onmessage = function ( event ) {
let params = event.data;
//postMessage( { result: recursiveFunction( HitCurrent, HitCurrent.playerCurrent, maxNodes ) } );
postMessage( { result: negaMax( ...params ) } );
};
` ], { type: "plain/text" } ) );
main();
}
I expect to get back the coordinates "a" and "b" of computed value by recursive function but it seems to return nothing.
I don't know how to receive the object HitTemp
object or more directly get a
and b
suggested coordinates ?
Feel free to ask me more precisions if you don't understand my problem in this algorithm.
There are two problems with the code that you presented:
postMessage
passes a reference, but instead it serializes/deserializes it.When you are using postMessage
from within main js into the WebWorker, you are passing the HitTemp object, and later on, in the WebWorker you assume, that if you set properties of that object, the the original object would be modified as well. By that I mean the following code:
firstWorker.postMessage([
HitTemp // <-- here you are sending the object to the WebWorker
, HitTemp.playerCurrent, maxNodes]);
workerScript = URL.createObjectURL( new Blob( [ `
// ...
if (e > evalFinal) {
HitCurrent.coordPlayable = [i,j]; // <-- here you access the object
evalFinal = e;
}
//...
Unfortunately, according to the documentation, when calling postMessage
, the original object is serialized by the caller and then deserialized in the WebWorker, so effectively, the WebWorker is operating on a copy. Fortunately, this can be easily avoided by posting the data you are most interested in inside the postMessage
back from the WebWorker.
a
and b
variables outside the scopeI noticed that you are trying to access values that are defined in the .then()
result callback outside that callback, like so:
//...
} ).then( ( { result } ) => {
let [ a, b ] = //here you define a and b
} );
// a and b are no longer in scope here
for (k = 0; k < 8; k++) {
exploreHitLine(HitCurrent, a, b, k, 'drawing');
}
To solve the first problem you need to return the values of HitCurrent
(which contain coordPlayable
that you are probably most interested in) with the postMessage
back from the WebWorker. For the second problem, just move the final for
loop using the a
and b
variables inside the .then()
callback. The result code is as follows:
Main js code:
new Promise( resolve => {
let HitTemp = JSON.parse(JSON.stringify(HitCurrent));
let firstWorker = new Worker( workerScript );
firstWorker.onmessage = function ( event ) {
resolve( event.data );
console.log('here5');
}
firstWorker.postMessage([HitTemp, HitTemp.playerCurrent, maxNodes]);
} ).then( ( { result } ) => {
var HitResult = result.HitResult;
let [ a, b ] = HitResult.coordPlayable; // <-- get values from result
console.log('result3 : ', result.eval);
console.log('here3 : ', a, b);
//move for loop inside the callback
for (k = 0; k < 8; k++) {
exploreHitLine(HitCurrent, a, b, k, 'drawing');
}
} );
WebWorker:
window.onload = function() {
// Inline webworker version
workerScript = URL.createObjectURL( new Blob( [ `
"use strict";
// ...
function negaMax(HitCurrent, colorCurrent, depth) {
// Indices
var i, j, k;
// Evaluation
var arrayTemp, evalFinal, e;
// Set current color to HitCurrent
HitCurrent.playerCurrent = colorCurrent;
// Deep copy of arrayCurrent array
arrayTemp = JSON.parse(JSON.stringify(HitCurrent.arrayCurrent));
// If depth equal to 0
if (depth == 0)
return evaluation(HitCurrent);
// Starting evaluation
evalFinal = -infinity;
// Compute all playable hits and check if playable hits
if (computeHit(HitCurrent, 'playable')) {
// Browse all possible hits
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
if (HitCurrent.arrayPlayable[i][j] == 'playable') {
for (k = 0; k < 8; k++) {
// Explore line started from (i,j) with direction "k"
exploreHitLine(HitCurrent, i, j, k, 'drawing');
}
// Recursive call
e = -negaMax(JSON.parse(JSON.stringify(HitCurrent)).eval, ((JSON.stringify(HitCurrent.playerCurrent) == JSON.stringify(playerBlack)) ? playerWhite : playerBlack), depth-1); //since negaMax returns an object, don't forget to access the value in the recursive call
if (e > evalFinal) {
HitCurrent.coordPlayable = [i,j];
evalFinal = e;
}
if (e == -infinity) {
HitCurrent.coordPlayable = [i,j];
}
// Restore arrayCurrent array
HitCurrent.arrayCurrent = JSON.parse(JSON.stringify(arrayTemp));
}
// Clean playable hits once double loop is done
cleanHits('playable', HitCurrent);
}
console.log('here2 :', evalFinal);
return {eval: evalFinal, HitResult: HitCurrent }; //<-- send the additional HitCurrent as a result here
}
onmessage = function ( event ) {
let params = event.data;
postMessage( { result: negaMax( ...params ) } );
};
` ], { type: "plain/text" } ) );
main();
}
I decided to return the whole HitCurrent
from inside the WebWorker and passed is as HitResult
parameter, because based on the fact that other parameters are modified by the recursive method as well (like arrayPlayable
and arrayCurrent
), then you would also be able to get the modified values after the calculation.