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:

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.

Upvotes: 3

Views: 240

Answers (2)

I've opened a GitHub issue with a simplified version of the problem: https://github.com/lodash/lodash/issues/4287.

Short answer given by lodash creator John-David Dalton:

With fp composition the accumulator is not create fresh for each composed reduce function. This is why create a mutationless version of the accumulator with fp.concat() works.

Upvotes: 1

Naor Tedgi
Naor Tedgi

Reputation: 5707

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

Upvotes: 1

Related Questions