Search code examples
javascriptfunctional-programminglodash2048

2048 : Strange behaviour of reduceRight in map with lodash/fp


The following code is the beginning of an attempt to make my version of 2048 (the game) using lodash-fp. I'm used to regular lodash, but this is my first contact with the fp flavor.

It implements the action of pushing to the right the tiles of one row, using two functions:

  • slide pushes the row to the right, provided there are empty spaces (represented by the value 0). In effect, this amounts to putting all the zeros on the left.
  • merge merges pairs of contiguous tiles of same value, generating an empty space and a new tile of doubled value. (slide is called a second time after merge to clean up the zeros that may result from that step).

The functions use the _.reduceRight method, which iterates over the tiles from right to left.

import _ from "lodash/fp";


let game = [[2, 2, 0, 0], [4, 0, 4, 0], [8, 0, 0, 8], [16, 16, 0, 0]]; // Sample state

let slide = _.flow(
  _.reduceRight(
    (v, acc) =>
      v === 0 ? [_.concat(acc[0], v), acc[1]] : [acc[0], _.concat(v, acc[1])],
    [[], []]
  ),
  _.flatten
);

let merge = _.flow(
  _.reduceRight(
    (v, acc) => {
      acc[0].unshift(v);
      if (acc[0].length === 2) {
        if (acc[0][0] === acc[0][1]) {
          acc[1] = _.concat(0, _.concat(acc[0][0] + acc[0][1], acc[1]));
          acc[0] = [];
        } else {
          acc[1] = _.concat(acc[0].pop(), acc[1]);
        }
      }
      return acc;
    },
    [[], []]
  ),
  _.flatten
);

// Moves one line
let moveLine = _.flow(
  slide,
  merge,
  slide
);

// Moves the 4 lines
let moveBoard = _.map(moveLine);

moveLine seems to work well. For instance, moveLine(game[0]) transforms [2, 2, 0, 0] into [0, 0, 0, 4].

Strangely, moveBoard(game) (which maps moveLine over the 4 rows) gives a weird result, getting longer on each iteration, as though the results from the previous steps were appended:

[
  [0,0,0,4],
  [0,0,0,0,0,0,8,4],
  [0,0,0,0,0,0,0,0,0,16,8,4],
  [0,0,0,0,0,0,0,0,0,0,0,0,32,16,8,4]
]

I see that the problem comes from merge, but I really fail to see what's going on here.


Solution

  • change move method to remove the first 4 elements.

    for some weird reason for each iteration of merge

    the acc[1] of ReduceRight as the previous array in it

    this patch will fix it

    let take = arr => arr.slice(0,4);
    
    // Moves one line
    
    let moveLine = _.flow(
      slide,
      merge,
      take
    );
    

    here is a runkit with the implementation

    https://runkit.com/naor-tedgi/5ccf0862b0da69001a7db546