Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Solver in 0 clicks:

    [...document.querySelectorAll("#game > div > div")].forEach(e => e.style.transform="rotate(0deg)")
It doesn't win the game, but can at least save the "preview" clicks.


An actual solver in Python:

    import numpy as np
    from mip import Model, xsum, minimize, BINARY

    def solve(tiles):
        rotations = [[[] for _ in row] for row in tiles]
        rot_vars = [[[] for _ in row] for row in tiles]
        model = Model("rotaboxes")
        for i, row in enumerate(tiles):
            for j, tile in enumerate(row):
                for _ in range(3):
                    tile = tile.transpose((1, 0, 2))[::-1]
                    rotations[i][j].append(tile)
                    rot_vars[i][j].append(model.add_var(var_type=BINARY))
                model += (xsum(rot_vars[i][j]) == 1)
        costs = []
        for i, row in enumerate(tiles):
            for j, tile in enumerate(row):
                for (di, dj) in [(0, 1), (1, 0)]:
                    if (i + di >= len(tiles)) or (j + dj >= len(tiles[0])):
                        continue
                    for r0, t0 in enumerate(rotations[i][j]):
                        for r1, t1 in enumerate(rotations[i + di][j + dj]):
                            if di:
                                a, b = t0[-1], t1[0]
                            else:
                                a, b = t0[:,-1], t1[:,0]
                            n = np.square(a - b).mean()
                            d = np.square(a[1:] - a[:1]).mean() + np.square(b[1:] - b[:1]).mean()
                            e = 2. * n / (d + 0.01)
                            v = model.add_var(var_type=BINARY)
                            model += (v >= rot_vars[i][j][r0] + rot_vars[i + di][j + dj][r1] - 1)
                            costs.append(e * v)
        model.objective = minimize(xsum(costs))
        model.optimize()
        solution = [[[r for v, r in zip(vs, rs) if v.x >= 0.99][0] for vs, rs in zip(row0, row1)] for row0, row1 in zip(rot_vars, rotations)]
        return solution


Actual solver that does win the game:

    document.querySelectorAll("#game > div > div").forEach(e => {
        const rotation = e.style.transform.match(/(\d+)/)[0];
        const clicks = 4-(rotation/90)%4;
        [...Array(clicks)].forEach(i=>e.click());
    });
It could be optimized to change the rotation direction depending on whether the current rotation is smaller or larger than 180deg, but that's not really necessary since running the script only counts as one click, and generates negative "overspin" numbers ;-)


Instead of the array spread syntax, why not:

    for(let i=0; i<clicks; i++){ e.click() };


Sure, it's the same. For some reason I don't like the 'for' syntax much, or having curly brackets on the same line. But to each their own.


What does […Array mean in this context? Does it create an array?


... is the spread syntax, which is a neat way to use the entries of an existing array or object in an argument list or within an array or object literal.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

Array(length) creates an array that has a length but no elements (or weird, "empty" elements). The square brackets create a new array [...Array(length)] which will have undefined elements instead, as trying to access an "empty" element returns undefined.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guid...


That is neat! Thanks!


The "..." is called a spread operator in javascript.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

(In python there is the "*" operator which should be equivalent: [1, 2, *arr].

Array(clicks) instantiate an array of length 'clicks', with its elements undefined.

Then "[...Array(clicks)]" takes the above array instance and spreads it as content in another array.

Seems redundant and they could've just used Array(clicks).


> Array(clicks) instantiate an array of length 'clicks', with its elements undefined.

Not undefined elements but no elements, or "empty" elements.

> they could've just used Array(clicks).

No, because the following forEach call does nothing for such an array. You need to fill the array first with Array(clicks).fill() or shorter, [...Array(clicks)]


Yes it creates an array of length 'clicks'; so for example [...Array(2)] creates the array [undefined, undefined]. It's just a way to do a loop without writing a for loop and use forEach instead.


Probably a shortcut for Array(n).fill()


Just wondering - why are you spreading the result to an array?

  document.querySelectorAll("#game > div > div").forEach(e => e.style.transform="rotate(0deg)")
Works too.


You're right!

(But since querySelectorAll returns a NodeList and not a regular array, some methods are unavailable, so I just have this habit of making it an array. But for iteration that's indeed not needed.)


Now you should count keystrokes for your type of solution.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: