1

In the context of a 2 players game, I have a C function that does recursive calls and for each call, it does a memcpy from a global array variable a current local array. Here's this C function ("game" array is declared as a global variable, outside the function) :

int game[8][8];

int recursive_function(int player, int *mx, int *my, int depth)                           

{                                                                                        
  int x, y;
  int eval, e;                                                                           
  int mx2, my2;                                                                          
  int game2[8][8];

  // TERMINAL CASE
  if (depth == 0) {
    return 1;
  }

  memcpy(game2, game, sizeof(game));                                                     
  eval = -INFINITY;                                                                        

  for (x = 0; x < 8; x++)       
    for (y = 0; y < 8; y++) {
      if (isplayabe((x, y, player)) {                                                    
        e = -recursive_function(OTHER(player), &mx2, &my2, depth-1);                          
      }
      if (e > eval) {                                                                    
        *mx = x; *my = y;
        eval = e;
      }                                                                                  
      memcpy(game, game2, sizeof(game));                                                 
      }                                                                                  
  return eval;
}

Now, I would like to implement the same function in javascript.

I think I have to use object for reference passing. So I have created objects Hit and HitTemp with for each of them, coordinates of array as attributes :

  1. Hit.coordPlaybles are (x,y) coordinates and makes the link as the equivalent of game variable above
  2. HitTemp.coordPlaybles are (x,y) coordinates and makes the link as the equivalent of game2 variable above

From this, I tried with Object Hit defined by :

// Player white and black
var playerWhite = ['white', 'black'];
var playerBlack = ['black', 'white'];

var Hit = {
 currentPlayer: playerBlack,
 coordPlayable: ['0', '0'],
 coordCurrent: ['0', '0'],
};

The object Hit is declared outside recursive_function (global variable) whereas HitTemp is defined only into this function (local variable).

Here is what I did :

// Object Hit passed by reference
function recursive_function(Hit, depth) {
  // Evaluation
  var eval, e;

   // TERMINAL CASE
      if (depth == 0) {
        return 1;
      }

  // Local Hit object : EQUIVALENT OF "memcpy" ??
  var HitTemp = Object.assign({}, Hit);

  eval = -infinity;       

  for (var x = 0; x < 8; x++)
    for (var y = 0; y < 8; y++) {
      if (isplayabe((x, y, HitTemp)) { 
        // CALL RECURSIVE WITH OPPOSITE PLAYER (SWITCHING PLAYER)                                                   
        e = -recursive_function(OTHER(HitTemp.currentPlayer), depth-1);  
        if (e > eval) {
                  // HitTemp.coordCurrent[0] = *mx
                  // HitTemp.coordPlayable[0] = x
                  // HitTemp.coordCurrent[1] = *my
                  // HitTemp.coordPlayable[1] = y
                  HitTemp.coordCurrent[0] = HitTemp.coordPlayable[0];
                  HitTemp.coordCurrent[1] = HitTemp.coordPlayable[1];
                  eval = e;
                }
   // Final copy from HitTemp to Hitobject : Here also, equivalent to 
   // the final memcpy of C version ??
   Hit = Object.assign({}, HitTemp);
  }
return eval;
}

Unfortunately, the results between the 2 versions are quite different (I am sure that C version is working fine).

Could anyone tell me if the Javascript "assign" method is the right method to reproduce the behavior of C "memcpy" function ?

If not, could you give me some clues to fix it with recursive calls?

UPDATE 1 :

Thanks for your help. However, I have an issue with solution suggested by @Siltaar . Indeed, if I do a deep copy with JSON.parse(JSON.stringify(HitCurrent)) as :

// Global variables : Player white and black
var playerWhite = ['white', 'black'];
var playerBlack = ['black', 'white'];

//Global variable Hit 
var Hit = {
 currentPlayer: playerBlack,
 coordPlayable: ['0', '0'],
 coordCurrent: ['0', '0'],
};

function recursive_function(HitCurrent, depth) {

// Deep copy
var HitTemp = JSON.parse(JSON.stringify(HitCurrent));

// Switch color for player

// BEFORE
console.log('BEFORE = ', HitTemp.currentPlayer);

// Switch
HitTemp.currentPlayer = (HitTemp.currentPlayer == playerBlack) ? playerWhite : playerBlack;

// AFTER : expected the opposite of BEFORE
console.log('AFTER = ', HitTemp.currentPlayer);

...
}

and I get with BEFORE and AFTER :

BEFORE =  
Array [ "black", "white" ]

AFTER =  
Array [ "black", "white" ]

As you can see, the value of "HitTemp.currentPlayer" is not switched, it seems that "HitTemp.currentPlayer == playerBlack" is set to false whereas the "BEFORE" value of "HitTemp.currentPlayer" is set to "playerBlack".

I must make notice that recursive_function is called with the global object Hit in the main() like :

    //Global variable Hit 
    var Hit = {
     currentPlayer: playerBlack,
     coordPlayable: ['0', '0'],
     coordCurrent: ['0', '0'],
    };

    // Main function
    function main() {

        recursive_function(Hit, maxDepth);
    ...

    }

Has this issue a relation with the deep copy of HitCurrent to HitTemp object ?

UPDATE 2:

If I use only one "=" (and not two "==") like this :

console.log('BEFORE = ', HitTemp.currentPlayer);
HitTemp.currentPlayer = (HitTemp.currentPlayer = playerBlack) ? playerWhite : playerBlack;
console.log('AFTER = ', HitTemp.currentPlayer);

Then the switching works : What's the right syntax (one "=" symbol or two ) with the condition of ternary operator to test an equality ?

  • Does your C code do a shallow copy or a deep copy? – Andy Gaskell Mar 03 '18 at 14:39
  • -@Andy Gaskell . I think that C code performs a deep copy between **game** and **game2** arrays and Ideally, I would like to clone the object **Hit** since I want to have the current state of game for each recursive call. –  Mar 03 '18 at 14:51

2 Answers2

0

Object.assign performs a shallow copy. Based on your comment above it looks like you want a deep copy. If you know that this data structure will not change then something like this will work:

var HitTemp = Object.assign({}, Hit, {
    coordPlayable: Hit.coordPlayable.slice(0),
    coordCurrent: Hit.coordCurrent.slice(0)
});

If you want a cleaner, more general solution check out cloneDeep from lodash. Since this is a game and performance may be a concern this question is worth checking out.

Andy Gaskell
  • 31,495
  • 6
  • 74
  • 83
  • As an extra note, although it may be less readable, we could also use `Object.assign` with arrays. `coordPlayable: Object.assign([], Hit.coordPlayable)` – גלעד ברקן Mar 03 '18 at 15:57
0

MDN Object.assign() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Warning for Deep Clone

For deep cloning, we need to use other alternatives because Object.assign() copies property values. If the source value is a reference to an object, it only copies that reference value.

In your case the currentPlayer: playerBlack is not deep copied.

Solutions like :

obj = JSON.parse(JSON.stringify(o));

Have been advised here : What is the most efficient way to deep clone an object in JavaScript?

Regarding UPDATE 1 : you compare two differents objects, they are copies with same values, but different objects in memory. Try to only compare strings or integers to differentiates your players. – Siltaar 2 mins ago edit

Regardine UPDATE 2 : the syntax with only one equal sign "=" is an attribution statement. Attribution usually return true. You're trying to make a comparison, so I would recommande to stick with "==" or "===" (if object type matters).

Siltaar
  • 173
  • 1
  • 7
  • -@Siltaar . thanks for your answer but having another problem with your solution, could you see please my **UPDATE 1** and **UPDATE 2** ? regards –  Mar 04 '18 at 12:51